compile.xml 100644 0 0 3432 7575735642 10365 0 ustar 0 0
javadoc.xml 100644 0 0 3372 7575735642 10347 0 ustar 0 0
バグやドキュメントの誤りの報告は作者宛てにお願いします。
]]>
ドキュメント内に含まれる社名、製品名については一般に各社の商標または登録商標です。
]]>
Copyright © 2001-2002 Michel Ishizuka. All Rights Reserved.
]]>
jp/gr/java_conf/dangan/io/BitDataBrokenException.java 100644 0 0 12034 7574235000 20210 0 ustar 0 0 //start of BitDataBrokenException.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* BitDataBrokenException.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
//import exceptions
import java.io.IOException;
import java.lang.Throwable;
/**
* EndOfStream に達してしまったため要求されたビット数の
* データを得られなかった場合に投げられる例外。
* jp.gr.java_conf.dangan.io.BitInputStream 用であるため、
* 保持しておける データは 32ビットまでとなっている点に
* 注意すること。
* NotEnoughBitsException と違い、こちらの例外を投げる
* 場合には 実際に読み込み動作を行ってしまっているため
* 読み込み位置は例外を投げる前の時点から変化してしまっ
* ている点に注意すること。
*
*
* -- revision history --
* $Log: BitDataBrokenException.java,v $
* Revision 1.1 2002/12/07 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [maintenance]
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class BitDataBrokenException extends IOException{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private Throwable cause
// private int bitData
// private int bitCount
//------------------------------------------------------------------
/**
* ビットデータが途中までしか
* 取得できない原因となった例外
*/
private Throwable cause;
/**
* 途中までのビットデータ
*/
private int bitData;
/**
* bitData の有効ビット数
*/
private int bitCount;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private BitDataBrokenException()
// public BitDataBrokenException( Throwable cause,
// int bitData, int bitCount )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可
*/
private BitDataBrokenException(){ }
/**
* 新しい BitDataBrokenException を構築する。
*
* @param cause ビットデータが途中までしか取得できない
* 原因となった例外
* @param bitData 要求されたビット数に満たないビットデータ
* @param bitCount bitData のビット数
*
*/
public BitDataBrokenException( Throwable cause,
int bitData,
int bitCount ){
this.cause = cause;
this.bitData = bitData;
this.bitCount = bitCount;
}
//------------------------------------------------------------------
// access method
//------------------------------------------------------------------
// public Throwable getCause()
// public int getBitData()
// public int getBitCount()
//------------------------------------------------------------------
/**
* ビットデータが途中までしか
* 取得できない原因となった例外を得る。
*
* @return 原因となった例外
*/
public Throwable getCause(){
return this.cause;
}
/**
* 要求されたビット数に満たない
* "壊れた" ビットデータを得る。
*
* @return ビットデータ
*/
public int getBitData(){
return this.bitData;
}
/**
* getBitData() で得られる
* ビットデータの有効ビット数を得る。
*
* @return ビットデータの有効ビット数
*/
public int getBitCount(){
return this.bitCount;
}
}
// end of BitDataBrokenException.java
jp/gr/java_conf/dangan/io/BitInputStream.java 100644 0 0 117061 7574235000 16620 0 ustar 0 0 //start of BitInputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* BitInputStream.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
import java.io.InputStream;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.lang.NullPointerException;
import java.lang.IllegalArgumentException;
import jp.gr.java_conf.dangan.io.BitDataBrokenException;
import jp.gr.java_conf.dangan.io.NotEnoughBitsException;
/**
* ビット入力のためのユーティリティクラス。
*
*
* -- revision history --
* $Log: BitInputStream.java,v $
* Revision 1.5 2002/12/07 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.4 2002/11/15 00:00:00 dangan
* [improvement]
* prefetchBits() が 32bit の読み込みを保証するように修正
* [change]
* メソッド名の変更
* prefetchBit -> peekBit
* prefetchBoolean -> peekBoolean
* prefetchBits -> peekBits
*
* Revision 1.3 2002/11/02 00:00:00 dangan
* [bug fix]
* available() availableBits() で
* ブロックせずに読み込める量よりも大きい値を返していた。
*
* Revision 1.2 2002/09/05 00:00:00 dangan
* [change]
* EndOfStream に達した後の read( new byte[0] ) や
* read( byte[] buf, int off, 0 ) の戻り値を
* InputStream と同じく 0 になるようにした
*
* Revision 1.1 2002/09/04 00:00:00 dangan
* [bug fix]
* skip( len ) と skipBits( len ) で len が 0 未満のとき
* 正しく処理できていなかった。
*
* Revision 1.0 2002/09/03 00:00:00 dangan
* add to version control
* [bug fix]
* mark() で 接続された in に渡す readLimit の計算が甘かったため、
* 要求された readLimit に達する前にマーク位置が破棄される事があった。
* EndOfStream に達した後の skip() および skip( 0 ) が -1 を返していた。
* [maintenance]
* タブ廃止
* ライセンス文の修正
*
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.5 $
*/
public class BitInputStream extends InputStream{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// default
//------------------------------------------------------------------
// private static final int DefaultCacheSize
//------------------------------------------------------------------
/**
* デフォルトのキャッシュサイズ
*/
private static final int DefaultCacheSize = 1024;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private InputStream in
//------------------------------------------------------------------
/**
* 接続された入力ストリーム
*/
private InputStream in;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// cache
//------------------------------------------------------------------
// private byte[] cache
// private int cacheLimit
// private int cachePosition
//------------------------------------------------------------------
/**
* 速度低下抑止用バイト配列
*/
private byte[] cache;
/**
* cache 内の有効バイト数
*/
private int cacheLimit;
/**
* cache 内の現在処理位置
*/
private int cachePosition;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// bit buffer
//------------------------------------------------------------------
// private int bitBuffer
// private int bitCount
//------------------------------------------------------------------
/**
* ビットバッファ。
* ビットデータは最上位ビットから bitCount だけ格納されている。
*/
private int bitBuffer;
/**
* bitBuffer の 有効ビット数
*/
private int bitCount;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// backup for mark/reset
//------------------------------------------------------------------
// private boolean markPositionIsInCache
// private byte[] markCache
// private int markCacheLimit
// private int markCachePosition
// private int markBitBuffer
// private int markBitCount
//------------------------------------------------------------------
/**
* mark位置がキャッシュの範囲内にあるかを示す。
* markされたとき true に設定され、
* 次に in から キャッシュへの読み込みが
* 行われたときに false に設定される。
*/
private boolean markPositionIsInCache;
/** cache の バックアップ用 */
private byte[] markCache;
/** cacheLimit のバックアップ用 */
private int markCacheLimit;
/** cachePosition のバックアップ用 */
private int markCachePosition;
/** bitBuffer のバックアップ用 */
private int markBitBuffer;
/** bitCount のバックアップ用 */
private int markBitCount;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private BitInputStream()
// public BitInputStream( InputStream in )
// public BitInputStream( InputStream in, int CacheSize )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private BitInputStream(){ }
/**
* 入力ストリーム in からのデータをビット単位で
* 読み込めるようなストリームを構築する。
*
* @param in 入力ストリーム
*/
public BitInputStream( InputStream in ){
this( in, BitInputStream.DefaultCacheSize );
}
/**
* 入力ストリーム in からのデータをビット単位で
* 読み込めるようなストリームを構築する。
*
* @param in 入力ストリーム
* @param CacheSize バッファサイズ
*/
public BitInputStream( InputStream in, int CacheSize ){
if( in != null && 4 <= CacheSize ){
this.in = in;
this.cache = new byte[ CacheSize ];
this.cacheLimit = 0;
this.cachePosition = 0;
this.bitBuffer = 0;
this.bitCount = 0;
this.markPositionIsInCache = false;
this.markCache = null;
this.markCacheLimit = 0;
this.markCachePosition = 0;
this.markBitBuffer = 0;
this.markBitCount = 0;
}else if( in == null ){
throw new NullPointerException( "in" );
}else{
throw new IllegalArgumentException( "CacheSize must be 4 or more." );
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
// public long skip( long length )
//------------------------------------------------------------------
/**
* 接続されたストリームから 8ビットのデータを読み込む。
*
* @return 読み出された 8ビットのデータ。
* 既に EndOfStream に達している場合は -1
*
* @exception IOException
* 接続された入力ストリームで
* 入出力エラーが発生した場合
* @exception BitDataBrokenException
* EndOfStreamに達したため
* 要求されたビット数のデータの
* 読み込みに失敗した場合。
*/
public int read() throws IOException {
try{
return this.readBits( 8 ); //throws LocalEOFException BitDataBrokenException IOException
}catch( LocalEOFException exception ){
if( exception.thrownBy( this ) ) return -1;
else throw exception;
}
}
/**
* 接続された入力ストリームから バイト配列 buffer を
* 満たすようにデータを読み込む。
* データは必ずしも buffer を満たすとは限らないことに注意。
*
* @param buffer 読み込まれたデータを格納するためのバイト配列
*
* @return buffer に読み込んだデータ量をバイト数で返す。
* 既に EndOfStream に達していた場合は -1 を返す。
*
* @exception IOException
* 接続された入力ストリームで
* 入出力エラーが発生した場合
* @exception BitDataBrokenException
* EndOfStreamに達したため
* 要求されたビット数のデータの
* 読み込みに失敗した場合。
*/
public int read( byte[] buffer ) throws IOException {
return this.read( buffer, 0, buffer.length ); //throws BitDataBrokenException IOException
}
/**
* 接続された入力ストリームから バイト配列 buffer の
* index で指定された位置から length バイトのデータを
* 読み込む。
* このメソッドは lengthバイト読み込むか、
* EndOfStream に到達するまでブロックする。
* データは必ずしも length バイト読み込まれるとは限ら
* ないことに注意。
*
* @param buffer 読み込まれたデータを格納するためのバイト配列
* @param index buffer内のデータ読み込み開始位置
* @param length bufferに読み込むデータ量
*
* @return buffer に読み込んだデータ量をバイト数で返す。
* 既に EndOfStream に達していた場合は -1 を返す。
*
* @exception IOException
* 接続された入力ストリームで
* 入出力エラーが発生した場合
* @exception BitDataBrokenException
* EndOfStreamに達したため
* 要求されたビット数のデータの
* 読み込みに失敗した場合。
*/
public int read( byte[] buffer, int index, int length ) throws IOException {
final int requested = length;
try{
while( 0 < length ){
buffer[index++] = (byte)this.readBits( 8 ); //throws LocalEOFException BitDataBrokenException IOException
length--;
}
return requested;
}catch( LocalEOFException exception ){
if( exception.thrownBy( this ) ){
if( requested != length ) return requested - length;
else return -1;
}else{
throw exception;
}
}catch( BitDataBrokenException exception ){
if( exception.getCause() instanceof LocalEOFException
&& ((LocalEOFException)exception.getCause()).thrownBy( this ) ){
this.bitBuffer >>>= exception.getBitCount();
this.bitCount += exception.getBitCount();
this.bitBuffer |= exception.getBitData() <<
( 32 - exception.getBitCount() );
return requested - length;
}else{
throw exception;
}
}
}
/**
* 接続された入力ストリームのデータを length バイト
* 読み飛ばす。
* このメソッドは lengthバイト読み飛ばすか、
* EndOfStream に到達するまでブロックする。
* データは必ずしも length バイト読み飛ばされるとは限ら
* ないことに注意。
*
* @param length 読み飛ばすバイト数。
*
* @return 実際に読み飛ばされたバイト数。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public long skip( long length ) throws IOException {
length = ( 0 < length ? length : 0 );
final long requested = length;
try{
while( 0 < length ){
this.readBits( 8 );
length--;
}
return requested;
}catch( LocalEOFException exception ){
return requested - length;
}catch( BitDataBrokenException exception ){
if( exception.getCause() instanceof LocalEOFException
&& ((LocalEOFException)exception.getCause()).thrownBy( this ) ){
this.bitBuffer >>>= exception.getBitCount();
this.bitCount += exception.getBitCount();
this.bitBuffer |= exception.getBitData() <<
( 32 - exception.getBitCount() );
return requested - length;
}else{
throw exception;
}
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupported()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*/
public void mark( int readLimit ){
readLimit -= this.cacheLimit - this.cachePosition;
readLimit -= this.bitCount / 8;
readLimit += 4;
readLimit = ( ( readLimit / this.cache.length ) * this.cache.length
+ ( readLimit % this.cache.length == 0 ? 0 : this.cache.length ) );
this.in.mark( readLimit );
if( this.markCache == null ){
this.markCache = (byte[])this.cache.clone();
}else{
System.arraycopy( this.cache, 0,
this.markCache, 0,
this.cacheLimit );
}
this.markCacheLimit = this.cacheLimit;
this.markCachePosition = this.cachePosition;
this.markBitBuffer = this.bitBuffer;
this.markBitCount = this.bitCount;
this.markPositionIsInCache = true;
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException
* (1) BitInputStream に mark がなされていない場合。
* (2) 接続された入力ストリームが markSupported()で
* false を返す場合。
* (3) 接続された入力ストリームで
* 入出力エラーが発生した場合。
* の何れか。
*/
public void reset() throws IOException {
if( this.markPositionIsInCache ){
this.cachePosition = this.markCachePosition;
this.bitBuffer = this.markBitBuffer;
this.bitCount = this.markBitCount;
}else if( !this.in.markSupported() ){
throw new IOException( "not support mark()/reset()." );
}else if( this.markCache == null ){ //この条件式は未だにマークされていないことを示す。コンストラクタで markCache が null に設定されるのを利用する。
throw new IOException( "not marked." );
}else{
//in が reset() できない場合は
//最初の行の this.in.reset() で
//IOException を投げることを期待している。
this.in.reset(); //throws IOException
System.arraycopy( this.markCache, 0,
this.cache, 0,
this.markCacheLimit );
this.cacheLimit = this.markCacheLimit;
this.cachePosition = this.markCachePosition;
this.bitBuffer = this.markBitBuffer;
this.bitCount = this.markBitCount;
}
}
/**
* 接続された入力ストリームが mark() と reset() を
* サポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public boolean markSupported(){
return this.in.markSupported();
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* 接続された入力ストリームからブロックしないで
* 読み込むことのできるバイト数を得る。
*
* @return ブロックしないで読み出せるバイト数。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public int available() throws IOException {
return this.availableBits() / 8; //throws IOException
}
/**
* この入力ストリームを閉じ、
* 使用していたリソースを開放する。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.in.close(); //throws IOException
this.in = null;
this.cache = null;
this.cacheLimit = 0;
this.cachePosition = 0;
this.bitBuffer = 0;
this.bitCount = 0;
this.markCache = null;
this.markCacheLimit = 0;
this.markCachePosition = 0;
this.markBitBuffer = 0;
this.markBitCount = 0;
this.markPositionIsInCache = false;
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int readBit()
// public boolean readBoolean()
// public int readBits( int count )
// public int skipBits( int count )
//------------------------------------------------------------------
/**
* 接続された入力ストリームから 1ビットのデータを
* 読み込む。
*
* @return 読み込まれた1ビットのデータ。
* 既にEndOfStreamに達している場合は -1。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public int readBit() throws IOException {
if( 0 < this.bitCount ){
int bit = this.bitBuffer >>> 31;
this.bitBuffer <<= 1;
this.bitCount -= 1;
return bit;
}else{
try{
this.fillBitBuffer();
int bit = this.bitBuffer >>> 31;
this.bitBuffer <<= 1;
this.bitCount -= 1;
return bit;
}catch( LocalEOFException exception ){
if( exception.thrownBy( this ) ){
return -1;
}else{
throw exception;
}
}
}
}
/**
* 接続された入力ストリームから 1ビットのデータを
* 真偽値として読み込む。
*
* @return 読み込まれた1ビットのデータが
* 1であれば true、0であれば false を返す。
*
* @exception EOFException 既にEndOfStreamに達していた場合
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public boolean readBoolean() throws IOException {
if( 0 < this.bitCount ){
boolean bool = ( this.bitBuffer < 0 );
this.bitBuffer <<= 1;
this.bitCount -= 1;
return bool;
}else{
this.fillBitBuffer();
boolean bool = ( this.bitBuffer < 0 );
this.bitBuffer <<= 1;
this.bitCount -= 1;
return bool;
}
}
/**
* 接続された入力ストリームから count ビットのデータを
* 読み込む。 戻り値が int値である事からも判るように
* 読み込むことのできる 最大有効ビット数は 32ビットで
* あるが、count は32以上の値を設定してもチェックを
* 受けないため それ以上の値を設定した場合は ビット
* データが読み捨てられる。
* たとえば readBits( 33 ) としたときは まず1ビットの
* データを読み捨て、その後の 32ビットのデータを返す。
* また count に 0以下の数字を設定して呼び出した場合、
* データを読み込む動作を伴わないため 戻り値は 常に0、
* EndOfStream に達していても EOFException を
* 投げない点に注意すること。
*
* @param count 読み込むデータのビット数
*
* @return 読み込まれたビットデータ。
*
* @exception IOException
* 接続された入力ストリームで
* 入出力エラーが発生した場合
* @exception EOFException
* 既にEndOfStreamに達していた場合
* @exception BitDataBrokenException
* 読み込み途中で EndOfStreamに達したため
* 要求されたビット数のデータの読み込み
* に失敗した場合。
*/
public int readBits( int count ) throws IOException {
if( 0 < count ){
if( count <= this.bitCount ){
int bits = this.bitBuffer >>> ( 32 - count );
this.bitBuffer <<= count;
this.bitCount -= count;
return bits;
}else{
final int requested = count;
int bits = 0;
try{
this.fillBitBuffer(); //throws LocalEOFException IOException
while( this.bitCount < count ){
count -= this.bitCount;
if( count < 32 ){
bits |= ( this.bitBuffer >>> ( 32 - this.bitCount ) ) << count;
}
this.bitBuffer = 0;
this.bitCount = 0;
this.fillBitBuffer(); //throws LocalEOFException IOException
}
bits |= this.bitBuffer >>> ( 32 - count );
this.bitBuffer <<= count;
this.bitCount -= count;
return bits;
}catch( LocalEOFException exception ){
if( exception.thrownBy( this ) && count < requested ){
throw new BitDataBrokenException( exception, bits >>> count, requested - count );
}else{
throw exception;
}
}
}
}else{
return 0;
}
}
/**
* 接続されたストリームから count ビットのデータを
* 読み飛ばす。
*
* @param count 読み飛ばしてほしいビット数
*
* @return 実際に読み飛びしたビット数
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public int skipBits( int count ) throws IOException {
count = Math.max( count, 0 );
if( count < this.bitCount ){
this.bitBuffer <<= count;
this.bitCount -= count;
return count;
}else{
final int requested = count;
count -= this.bitCount;
this.bitCount = 0;
this.bitBuffer = 0;
try{
while( ( this.cacheLimit - this.cachePosition ) * 8 <= count ){
count -= ( this.cacheLimit - this.cachePosition ) * 8;
this.cachePosition = this.cacheLimit;
this.fillCache();
if( this.cacheLimit == this.cachePosition ){
throw new LocalEOFException( this );
}
}
this.cachePosition += ( count >> 3 );
count = count & 0x07;
if( 0 < count ){
this.bitCount = 8 - count;
this.bitBuffer = this.cache[ this.cachePosition++ ] << ( 24 + count );
count = 0;
}
}catch( LocalEOFException exception ){
}
return requested - count;
}
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// prefetch
//------------------------------------------------------------------
// public int peekBit()
// public boolean peekBoolean()
// public int peekBits( int count )
//------------------------------------------------------------------
/**
* 読み込み位置を変えずに 1ビットのデータを先読みする。
*
* @return 読み込まれた1ビットのデータ。
* 既にEndOfStreamに達している場合は -1。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public int peekBit() throws IOException {
if( 0 < this.bitCount ){
return this.bitBuffer >>> 31;
}else{
try{
this.fillBitBuffer(); //throws LocalEOFException IOException
return this.bitBuffer >>> 31;
}catch( LocalEOFException exception ){
if( exception.thrownBy( this ) ){
return -1;
}else{
throw exception;
}
}
}
}
/**
* 読み込み位置を変えずに 1ビットのデータを
* 真偽値として先読みする。
*
* @return 読み込まれた1ビットのデータが
* 1であれば true、0であれば false を返す。
*
* @exception EOFException 既にEndOfStreamに達していた場合
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public boolean peekBoolean() throws IOException {
if( 0 < this.bitCount ){
return ( this.bitBuffer < 0 );
}else{
this.fillBitBuffer(); //throws LocalEOFException IOException
return ( this.bitBuffer < 0 );
}
}
/**
* 読み込み位置を変えずに count ビットのデータを先読みする。
* 戻り値が int型であることからもわかるように
* 最大有効ビット数は 32ビットである。
* EndOfStream 付近を除いて、先読み出来ることが保障されるのは
* 32ビットである。(ビットバッファの大きさが 32ビットであるため)
* もし 32ビット以上の先読み機能が必須となる場合は
* その都度 mark()、readBits()、reset() を繰り返すか、
* このクラスを使用することを諦めること。
*
* @param count 読み込むビット数
*
* @return 先読みした count ビットのビットデータ
*
* @exception EOFException
* 既にEndOfStreamに達していた場合
* @exception IOException
* 接続された入力ストリームで
* 入出力エラーが発生した場合
* @exception NotEnoughBitsException
* count が先読み可能な範囲外の場合
*/
public int peekBits( int count ) throws IOException {
if( 0 < count ){
if( count <= this.bitCount ){
return this.bitBuffer >>> ( 32 - count );
}else{
this.fillBitBuffer(); //throws LocalEOFException, IOException
if( count <= this.bitCount ){
return this.bitBuffer >>> ( 32 - count );
}else if( count <= this.cachedBits() ){
if( count <= 32 ){
int bits = this.bitBuffer;
bits |= ( this.cache[ this.cachePosition ] & 0xFF )
>> ( this.bitCount - 24 );
return bits >>> ( 32 - count );
}else if( count - 32 < this.bitCount ){
int bits = this.bitBuffer << ( count - 32 );;
int bcnt = this.bitCount - ( count - 32 );
int pos = this.cachePosition;
while( bcnt < 25 ){
bits |= ( this.cache[ pos++ ] & 0xFF ) << ( 24 - bcnt );
bcnt += 8;
}
if( bcnt < 32 ){
bits |= ( this.cache[ pos ] & 0xFF ) >> ( bcnt - 24 );
}
return bits;
}else{
count -= this.bitCount;
count -= 32;
int pos = this.cachePosition + ( count >> 3 );
count &= 0x07;
if( 0 < count ){
return ( this.cache[ pos ] << ( 24 + count ) )
| ( ( this.cache[ pos + 1 ] & 0xFF ) << ( 16 + count ) )
| ( ( this.cache[ pos + 2 ] & 0xFF ) << ( 8 + count ) )
| ( ( this.cache[ pos + 3 ] & 0xFF ) << count )
| ( ( this.cache[ pos + 4 ] & 0xFF ) >> ( 8 - count ) );
}else{
return ( this.cache[ pos ] << 24 )
| ( ( this.cache[ pos + 1 ] & 0xFF ) << 16 )
| ( ( this.cache[ pos + 2 ] & 0xFF ) << 8 )
| ( this.cache[ pos + 3 ] & 0xFF );
}
}
}else{
throw new NotEnoughBitsException( this.cachedBits() );
}
}
}else{
return 0;
}
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int availableBits()
// private int cachedBits()
//------------------------------------------------------------------
/**
* 接続された入力ストリームからブロックしないで
* 読み込むことのできるビット数を得る。
*
* @return ブロックしないで読み出せるビット数。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public int availableBits() throws IOException {
int avail = ( this.cacheLimit - this.cachePosition )
+ this.in.available() / this.cache.length * this.cache.length;//throws IOException
avail += this.bitCount - 32;
return Math.max( avail, 0 );
}
/**
* この BitInputStream 内に蓄えられているビット数を得る。
*
* @return この BitInputStream 内に蓄えられているビット数。
*/
private int cachedBits(){
return this.bitCount + ( ( this.cacheLimit - this.cachePosition ) << 3 );
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// fill
//------------------------------------------------------------------
// private void fillBitBuffer()
// private void fillCache()
//------------------------------------------------------------------
/**
* bitBuffer にデータを満たす。
* EndOfStream 付近を除いて bitBuffer には
* 25bit のデータが確保されることを保障する。
*
* @exception IOException 入出力エラーが発生した場合
* @exception LocalEOFException bitBuffer が空の状態で EndOfStream に達した場合
*/
private void fillBitBuffer() throws IOException {
if( 32 <= this.cachedBits() ){
if( this.bitCount <= 24 ){
if( this.bitCount <= 16 ){
if( this.bitCount <= 8 ){
if( this.bitCount <= 0 ){
this.bitBuffer = this.cache[this.cachePosition++] << 24;
this.bitCount = 8;
}
this.bitBuffer |= ( this.cache[this.cachePosition++] & 0xFF )
<< ( 24 - this.bitCount );
this.bitCount += 8;
}
this.bitBuffer |= ( this.cache[this.cachePosition++] & 0xFF )
<< ( 24 - this.bitCount );
this.bitCount += 8;
}
this.bitBuffer |= ( this.cache[this.cachePosition++] & 0xFF )
<< ( 24 - this.bitCount );
this.bitCount += 8;
}
}else if( this.bitCount < 25 ){
if( this.bitCount == 0 ){
this.bitBuffer = 0;
}
int count = Math.min( ( 32 - this.bitCount ) >> 3,
this.cacheLimit - this.cachePosition );
while( 0 < count-- ){
this.bitBuffer |= ( this.cache[this.cachePosition++] & 0xFF )
<< ( 24 - this.bitCount );
this.bitCount += 8;
}
this.fillCache(); //throws IOException
if( this.cachePosition < this.cacheLimit ){
count = Math.min( ( 32 - this.bitCount ) >> 3,
this.cacheLimit - this.cachePosition );
while( 0 < count-- ){
this.bitBuffer |= ( this.cache[this.cachePosition++] & 0xFF )
<< ( 24 - this.bitCount );
this.bitCount += 8;
}
}else if( this.bitCount <= 0 ){
throw new LocalEOFException( this );
}
}
}
/**
* cache が空になった時に cache にデータを読み込む。
*
* @exception IOException 入出力エラーが発生した場合
*/
private void fillCache() throws IOException {
this.markPositionIsInCache = false;
this.cacheLimit = 0;
this.cachePosition = 0;
//cache にデータを読み込む
int read = 0;
while( 0 <= read && this.cacheLimit < this.cache.length ){
read = this.in.read( this.cache,
this.cacheLimit,
this.cache.length - this.cacheLimit ); //throws IOException
if( 0 < read ) this.cacheLimit += read;
}
}
//------------------------------------------------------------------
// inner classes
//------------------------------------------------------------------
// private static class LocalEOFException
//------------------------------------------------------------------
/**
* BitInputStream 内で EndOfStream の検出に
* EOFException を使用するのは少々問題があるので
* ローカルな EOFException を定義する。
*/
private static class LocalEOFException extends EOFException {
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private Object owner
//------------------------------------------------------------------
/**
* この例外を投げたオブジェクト
*/
private Object owner;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// public LocalEOFException( Object object )
//------------------------------------------------------------------
/**
* コンストラクタ。
*
* @param object この例外を投げたオブジェクト
*/
public LocalEOFException( Object object ){
super();
this.owner = object;
}
//------------------------------------------------------------------
// access method
//------------------------------------------------------------------
// public boolean thrownBy( Object object )
//------------------------------------------------------------------
/**
* この例外が object によって投げられたかどうかを得る。
*
* @param object オブジェクト
*
* @return この例外が objectによって
* 投げられた例外であれば true
* 違えば false
*/
public boolean thrownBy( Object object ){
return this.owner == object;
}
}
}
//end of BitInputStream.java
jp/gr/java_conf/dangan/io/BitOutputStream.java 100644 0 0 33655 7573513400 17011 0 ustar 0 0 //start of BitOutputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* BitOutputStream.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
import java.io.OutputStream;
//import exceptions
import java.io.IOException;
import java.lang.NullPointerException;
import java.lang.IllegalArgumentException;
/**
* 接続された出力ストリームにビットデータを出力するための
* 出力ストリームクラス。
*
*
* -- revision history --
* $Log: BitOutputStream.java,v $
* Revision 1.1 2002/12/05 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/09/11 00:00:00 dangan
* add to version control
* [change]
* close() 後の write系メソッドと flush() で
* 例外を投げるように修正
* [maintenance]
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class BitOutputStream extends OutputStream{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// default
//------------------------------------------------------------------
// private static final int DefaultCacheSize
//------------------------------------------------------------------
/**
* デフォルトおキャッシュサイズ
*/
private static final int DefaultCacheSize = 1024;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private OutputStream out
//------------------------------------------------------------------
/**
* 接続された出力ストリーム
*/
private OutputStream out;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// cache
//------------------------------------------------------------------
// private byte[] cache
// private int cachePosition
//------------------------------------------------------------------
/**
* 速度低下抑止用バイト配列
*/
private byte[] cache;
/**
* cacheBuffer 内の現在処理位置
*/
private int cachePosition;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// bit buffer
//------------------------------------------------------------------
// private int bitBuffer
// private int bitCount
//------------------------------------------------------------------
/**
* ビットバッファ
*/
private int bitBuffer;
/**
* bitBuffer の 有効ビット数
*/
private int bitCount;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private BitOutputStream()
// public BitOutputStream( OutputStream out )
// public BitOutputStream( OutputStream out, int CacheSize )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private BitOutputStream(){ }
/**
* 出力ストリーム out へ データをビット単位で
* 書きこめるようなストリームを構築する。
* キャッシュサイズにはデフォルト値が使用される。
*
* @param out 出力ストリーム
*/
public BitOutputStream( OutputStream out ){
this( out, BitOutputStream.DefaultCacheSize );
}
/**
* 出力ストリーム out へ データをビット単位で
* 書きこめるようなストリームを構築する。
*
* @param out 出力ストリーム
* @param CacheSize キャッシュサイズ
*
* @exception IllegalArgumentException
* CacheSize が 4未満の場合、または
* CacheSize が 4の倍数で無い場合。
*/
public BitOutputStream( OutputStream out, int CacheSize ){
if( out != null && 4 <= CacheSize && 0 == ( CacheSize & 0x03 ) ){
this.out = out;
this.cache = new byte[ CacheSize ];
this.cachePosition = 0;
this.bitBuffer = 0;
this.bitCount = 0;
}else if( out == null ){
throw new NullPointerException( "out" );
}else if( CacheSize < 4 ){
throw new IllegalArgumentException( "CacheSize must be 4 or more." );
}else{
throw new IllegalArgumentException( "CacheSize must be multiple of 4." );
}
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* 接続された出力ストリームに 8ビットのデータを出力する。
*
* @param data 8ビットのデータ。
* 上位24ビットは無視される。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( int data ) throws IOException {
this.writeBits( 8, data );
}
/**
* 接続された出力ストリームにbufferの内容を連続した
* 8ビットのデータとして出力する。
*
* @param buffer 出力すべきデータを格納したバイト配列
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer ) throws IOException {
this.write( buffer, 0, buffer.length ); //throws IOException
}
/**
* 接続された出力ストリームにbufferのindexから
* lengthバイトの内容を連続した 8ビットのデータ
* として出力する。
*
* @param buffer 出力すべきデータを格納したバイト配列
* @param index buffer内のデータ開始位置
* @param length 出力するデータ量(バイト数)
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer, int index, int length )
throws IOException {
if( this.bitCount % 8 == 0 ){
this.flush(); //throws IOException
this.out.write( buffer, index, length ); //throws IOException
}else{
while( 0 < length-- )
this.writeBits( 8, buffer[index++] ); //throws IOException
}
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* このビット出力ストリームにバッファリングされている
* 8ビット単位のデータを全て出力先に出力する。
* 8ビットに満たないデータは出力されないことに注意。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void flush() throws IOException {
while( 8 <= this.bitCount ){
this.cache[ this.cachePosition++ ] = (byte)( this.bitBuffer >> 24 );
this.bitBuffer <<= 8;
this.bitCount -= 8;
}
this.out.write( this.cache, 0, this.cachePosition ); //throws IOException
this.cachePosition = 0;
this.out.flush(); //throws IOException
}
/**
* この出力ストリームと、接続された出力ストリームを閉じ、
* 使用していたリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
while( 0 < this.bitCount ){
this.cache[ this.cachePosition++ ] = (byte)( this.bitBuffer >> 24 );
this.bitBuffer <<= 8;
this.bitCount -= 8;
}
this.out.write( this.cache, 0, this.cachePosition ); //throws IOException
this.cachePosition = 0;
this.out.flush(); //throws IOException
this.out.close(); //throws IOException
this.out = null;
this.cache = null;
this.cachePosition = 0;
this.bitCount = 128;
this.bitBuffer = 0;
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// bit write
//------------------------------------------------------------------
// public void writeBit( int data )
// public void writeBoolean( boolean bool )
// public void writeBits( int count, int data )
//------------------------------------------------------------------
/**
* 接続された出力ストリームに1ビットのデータを出力する。
*
* @param data 1ビットのデータ。
* 上位31ビットは無視される。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void writeBit( int data ) throws IOException {
this.bitBuffer |= ( data & 0x00000001 ) << 31 - this.bitCount;
this.bitCount++;
if( 32 <= this.bitCount ) this.writeOutBitBuffer(); //throws IOException
}
/**
* 真偽値を接続された出力ストリームに1ビットの
* データとして出力する。
* true は 1、false は 0として出力する。
* java.io.DataOutput の writeBoolean() とは
* 互換性が無いので注意すること。
*
* @param bool 真偽値
*
* @exception IOException 入出力エラーが発生した場合
*/
public void writeBoolean( boolean bool ) throws IOException {
if( bool ) this.bitBuffer |= 1 << 31 - this.bitCount;
this.bitCount++;
if( 32 <= this.bitCount ) this.writeOutBitBuffer(); //throws IOException
}
/**
* 接続された出力ストリームにビットデータを出力する。
*
* @param count data の有効ビット数
* @param data ビットデータ
*
* @exception IOException 入出力エラーが発生した場合
*/
public void writeBits( int count, int data ) throws IOException {
while( 0 < count ){
int available = 32 - this.bitCount;
if( count < available ){
this.bitBuffer |= ( data & ( 0xFFFFFFFF >>> 32 - count ) )
<< available - count;
this.bitCount += count;
count = 0;
}else{
count -= available;
this.bitBuffer |= data >> count
& ( 0xFFFFFFFF >>> 32 - available );
this.writeOutBitBuffer();
}
}
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private void writeOutBitBuffer()
//------------------------------------------------------------------
/**
* ビットバッファに蓄えられたデータを全てキャッシュに
* 出力し、キャッシュが満ちた場合はキャッシュのデータを
* 接続された出力ストリームに出力する。
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOutBitBuffer() throws IOException {
this.cache[ this.cachePosition++ ] = (byte)( this.bitBuffer >> 24 );
this.cache[ this.cachePosition++ ] = (byte)( this.bitBuffer >> 16 );
this.cache[ this.cachePosition++ ] = (byte)( this.bitBuffer >> 8 );
this.cache[ this.cachePosition++ ] = (byte)this.bitBuffer;
this.bitBuffer = 0;
this.bitCount = 0;
if( this.cache.length <= this.cachePosition ){
this.out.write( this.cache );
this.cachePosition = 0;
}
}
}
//end of BitOutputStream.java
jp/gr/java_conf/dangan/io/Bits.java 100644 0 0 5567 7573513400 14560 0 ustar 0 0 //start of Bits.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* Bits.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
//import exceptions
/**
* ビット処理のためのユーティリティメソッド群。
*
*
* -- revision history --
* $Log: Bits.java,v $
* Revision 1.0 2002/12/05 00:00:00 dangan
* first edition
* add to version control
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class Bits{
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private Bits()
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private Bits(){ }
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// length of bits
//------------------------------------------------------------------
// public static int len( int val )
//------------------------------------------------------------------
/**
* val のビット長を得る。
* val は符号なし 32ビット整数とみなされる。
* log2( (long)val & 0xFFFFFFFFL ) の戻り値の
* 小数点以下を切り捨てたものと同等。
*
* @param val 整数値
*
* @return val のビット長
*/
public static int len( int val ){
int len = 0;
while( 0 != val ){
val >>>= 1;
len++;
}
return len;
}
}
//end of Bits.java
jp/gr/java_conf/dangan/io/CachedInputStream.java 100644 0 0 44567 7573513400 17245 0 ustar 0 0 //start of CachedInputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* CachedInputStream.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
import java.io.InputStream;
//import exceptions
import java.io.IOException;
import java.lang.IllegalArgumentException;
/**
* キャッシュを使用して高速化するための入力ストリーム。
* BufferedInputStream とは read系メソッドが synchronized
* されていないため、同期処理によるロスがない、mark/reset は
* キャッシュ内の読み込み位置の移動で行えるときのみサポートであり、
* それ以上は接続された入力ストリームの性能による、等の違いがある。
*
*
* -- revision history --
* $Log: CachedInputStream.java,v $
* Revision 1.3 2002/12/05 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.2 2002/11/02 00:00:00 dangan
* [bug fix]
* available() でブロックせずに読み込める量よりも大きい値を返していた。
*
* Revision 1.1 2002/09/05 00:00:00 dangan
* [change]
* EndOfStream に達した後の read( new byte[0] ) や
* read( byte[] buf, int off, 0 ) の戻り値を
* InputStream と同じく 0 になるようにした。
*
* Revision 1.0 2002/09/05 00:00:00 dangan
* add to version control
* [bug fix]
* mark() で 接続された in に渡す readLimit の計算が甘かったため、
* 要求された readLimit に達する前にマーク位置が破棄される事があった。
* read( buf, off, len ) 内の System.arraycopy の呼び出しで
* dst と src を逆にしていた。
* [change]
* EndOfStream に達した後の read( new byte[0] ) や
* read( buf, off,0 ) が -1 を返すように修正。
* [maintenance]
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.3 $
*/
public class CachedInputStream extends InputStream{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// default
//------------------------------------------------------------------
// private static final int DefaultCacheSize
//------------------------------------------------------------------
/**
* デフォルトのキャッシュサイズ
*/
private static final int DefaultCacheSize = 1024;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private InputStream in
//------------------------------------------------------------------
/**
* データを供給する入力ストリーム
*/
private InputStream in;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// cache
//------------------------------------------------------------------
// private byte[] cache
// private int cachePosition
// private int cacheLimit
//------------------------------------------------------------------
/**
* データを蓄えるためのキャッシュ
*/
private byte[] cache;
/**
* cache内の現在処理位置
*/
private int cachePosition;
/**
* cacheの読み込み限界位置
*/
private int cacheLimit;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// backup for mark/reset
//------------------------------------------------------------------
// private boolean markPositionIsInCache
// private byte[] markCache
// private int markCachePosition
// private int markCacheLimit
//------------------------------------------------------------------
/**
* mark位置がキャッシュの範囲内にあるかを示す。
* markされたとき true に設定され、
* 次に in から キャッシュへの読み込みが
* 行われたときに false に設定される。
*/
private boolean markPositionIsInCache;
/** cacheのバックアップ用 */
private byte[] markCache;
/** cachePositionのバックアップ用 */
private int markCachePosition;
/** cacheLimitのバックアップ用 */
private int markCacheLimit;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private CachedInputStream()
// public CachedInputStream( InputStream in )
// public CachedInputStream( InputStream in, int cacheSize )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private CachedInputStream(){ }
/**
* デフォルトのサイズのキャッシュを持つ
* CachedInputStreamを構築する。
*
* @param in キャッシュが必要な入力ストリーム
*
* @exception IllegalArgumentException
* in が null だった場合
*/
public CachedInputStream( InputStream in ){
this( in, CachedInputStream.DefaultCacheSize );
}
/**
* 指定されたサイズのキャッシュを持つ
* CachedInputStreamを構築する。
*
* @param in キャッシュが必要な入力ストリーム
* @param cacheSize キャッシュのサイズ
*
* @exception IllegalArgumentException
* cacheSize が 0以下であるか、
* in が null だった場合
*/
public CachedInputStream( InputStream in, int cacheSize ){
if( in != null && 0 < cacheSize ){
this.in = in;
this.cache = new byte[cacheSize];
this.cachePosition = 0;
this.cacheLimit = 0;
this.markPositionIsInCache = false;
this.markCache = null;
this.markCachePosition = 0;
this.markCacheLimit = 0;
}else if( in == null ){
throw new IllegalArgumentException( "in must not be null." );
}else{
throw new IllegalArgumentException( "cacheSize must be one or more." );
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
// public long skip( long length )
//------------------------------------------------------------------
/**
* 接続されたストリームから 1バイトのデータを
* 0〜255 にマップして読み込む。
*
* @return 読み出された 1バイトのデータを返す。
* 既に EndOfStreamに達していた場合は -1を返す。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public int read() throws IOException {
if( this.cachePosition < this.cacheLimit ){
return this.cache[ this.cachePosition++ ] & 0xFF;
}else{
this.fillCache(); //throws IOException
if( this.cachePosition < this.cacheLimit ){
return this.cache[ this.cachePosition++ ] & 0xFF;
}else{
return -1;
}
}
}
/**
* 接続されたストリームから bufferを満たすように
* データを読み込む。
* このメソッドは buffer を満たすまでデータを読み込むか、
* EndOfStreamに到達するまでブロックする。
*
* @param buffer 読み込んだデータを格納するためのバイト配列
*
* @return buffer に読み込んだデータ量をバイト数で返す。
* 既に EndOfStreamに達していた場合は -1を返す。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public int read( byte[] buffer ) throws IOException {
return this.read( buffer, 0, buffer.length );
}
/**
* 接続されたストリームから buffer に index で指定された
* 位置へ length バイトデータを読み込む。
* このメソッドは length バイト読み込むか、
* EndOfStreamに到達するまでブロックする。
*
* @param buffer 読み込んだデータを格納するためのバイト配列
* @param index buffer内のデータ読み込み開始位置
* @param length bufferに読み込むデータ量
*
* @return buffer に読み込んだデータ量をバイト数で返す。
* 既に EndOfStreamに達していた場合は -1を返す。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public int read( byte[] buffer, int index, int length )
throws IOException {
final int requested = length;
while( 0 < length ){
if( this.cacheLimit <= this.cachePosition ){
this.fillCache(); //throws IOException
if( this.cacheLimit <= this.cachePosition ){
if( requested == length ){
return -1;
}else{
break;
}
}
}
int copylen = Math.min( length,
this.cacheLimit - this.cachePosition );
System.arraycopy( this.cache, this.cachePosition,
buffer, index, copylen );
index += copylen;
length -= copylen;
this.cachePosition += copylen;
}
return requested - length;
}
/**
* 接続された入力ストリームのデータを length バイト読み飛ばす。
* このメソッドは length バイト読み飛ばすか
* EndOfStream に到達するまでブロックする。
*
* @param length 読み飛ばすバイト数。
*
* @return 実際に読み飛ばされたバイト数。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public long skip( long length ) throws IOException {
final long requested = length;
while( 0 < length ){
if( this.cacheLimit <= this.cachePosition ){
this.fillCache(); //throws IOException
if( this.cacheLimit <= this.cachePosition ){
break;
}
}
long skiplen = Math.min( length, (long)(this.cacheLimit - this.cachePosition) );
length -= skiplen;
this.cachePosition += (int)skiplen;
}
return requested - length;
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupported()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*/
public void mark( int readLimit ){
readLimit -= this.cacheLimit - this.cachePosition;
readLimit = ( readLimit / this.cache.length ) * this.cache.length
+ ( readLimit % this.cache.length == 0 ? 0 : this.cache.length );
this.in.mark( readLimit );
if( this.markCache == null ){
this.markCache = (byte[])this.cache.clone();
}else{
System.arraycopy( this.cache, 0, this.markCache, 0, this.cacheLimit );
}
this.markCacheLimit = this.cacheLimit;
this.markCachePosition = this.cachePosition;
this.markPositionIsInCache = true;
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException
* (1) CachedInputStream に mark がなされていない場合。
* (2) 接続された入力ストリームが markSupported()で
* false を返す場合。
* (3) 接続された入力ストリームで
* 入出力エラーが発生した場合。
* の何れか。
*/
public void reset() throws IOException {
if( this.markPositionIsInCache ){
this.cachePosition = this.markCachePosition;
}else if( !this.in.markSupported() ){
throw new IOException( "not support mark()/reset()." );
}else if( this.markCache == null ){ //この条件式は未だにマークされていないことを示す。コンストラクタで markCache が null に設定されるのを利用する。
throw new IOException( "not marked." );
}else{
//in が reset() できない場合は
//最初の行の this.in.reset() で
//IOException を投げることを期待している。
this.in.reset(); //throws IOException
System.arraycopy( this.markCache, 0, this.cache, 0, this.markCacheLimit );
this.cacheLimit = this.markCacheLimit;
this.cachePosition = this.markCachePosition;
}
}
/**
* 接続された入力ストリームが mark() と reset() を
* サポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public boolean markSupported(){
return this.in.markSupported();
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* 接続された入力ストリームからブロックしないで
* 読み込むことのできるバイト数を得る。
*
* @return ブロックしないで読み出せるバイト数。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public int available() throws IOException {
return this.cacheLimit - this.cachePosition
+ ( this.in.available() / this.cache.length ) * this.cache.length;//throws IOException
}
/**
* この入力ストリームを閉じ、使用していた
* 全てのリソースを開放する。
*
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.in.close(); //throws IOException
this.in = null;
this.cache = null;
this.cacheLimit = 0;
this.cachePosition = 0;
this.markCache = null;
this.markCacheLimit = 0;
this.markCachePosition = 0;
this.markPositionIsInCache = false;
}
//------------------------------------------------------------------
// local methods
//------------------------------------------------------------------
// private void fillCache()
//------------------------------------------------------------------
/**
* 必要がある場合に、キャッシュ用バッファにデータを
* 補填しキャッシュ用バッファに必ずデータが存在する
* ことを保証するために呼ばれる。
* もし EndOfStream まで読み込まれている場合は データが
* 補填されないことによって それを示す。
*
* @exception IOException 入出力エラーが発生した場合
*/
private void fillCache() throws IOException {
this.markPositionIsInCache = false;
this.cacheLimit = 0;
this.cachePosition = 0;
//キャッシュにデータを読み込み
int read = 0;
while( 0 <= read && this.cacheLimit < this.cache.length ){
read = this.in.read( this.cache,
this.cacheLimit,
this.cache.length - this.cacheLimit ); //throws IOException
if( 0 < read ) this.cacheLimit += read;
}
}
}
//end of CachedInputStream.java
jp/gr/java_conf/dangan/io/Disconnectable.java 100644 0 0 4664 7517367000 16572 0 ustar 0 0 //start of Disconnectable.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* Disconnectable.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
//import exceptions
import java.io.IOException;
/**
* 接続を解除できるストリームのための
* インターフェイス。
* このインターフェイスを実装するストリームは
* close() は disconnect() を呼ぶべきである。
*
*
* -- revision history --
* $Log: Disconnectable.java,v $
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [maintenance]
* タブ廃止
* ライセンス文の修正
* ソース整備
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public interface Disconnectable{
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// public abstract void disconnect()
//------------------------------------------------------------------
/**
* 接続されていたストリームとの接続を解除する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract void disconnect() throws IOException;
}
//end of Disconnectable.java
jp/gr/java_conf/dangan/io/DisconnectableInputStream.java 100644 0 0 23162 7517367000 21000 0 ustar 0 0 //start of DisconnectableInputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* DisconnectableInputStream.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
import java.io.InputStream;
import jp.gr.java_conf.dangan.io.Disconnectable;
//import exceptions
import java.io.IOException;
import java.lang.NullPointerException;
/**
* データを供給する入力ストリームと データを処理する
* 入力ストリームとの接続を解除するためのユーティリティクラス。
* java.io.BufferedInputStream 等のバッファリングするストリーム
* との接続を解除する場合は
* jp.gr.java_conf.dangan.io.LimitedInputStream 等を使用して
* 接続解除位置を過ぎたバッファリングを抑止する必要がある。
*
*
* -- revision history --
* $Log: DisconnectableInputStream.java,v $
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [maintenance]
* タブ廃止
* ライセンス文の修正
* ソース整備
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class DisconnectableInputStream extends InputStream
implements Disconnectable {
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private InputStream in
//------------------------------------------------------------------
/**
* 接続された入力ストリーム
*/
private InputStream in;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private DisconnectableInputStream()
// public DisconnectableInputStream( InputStream in )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private DisconnectableInputStream(){ }
/**
* in との接続を解除可能な入力ストリームを構築する。
*
* @param in 入力ストリーム
*/
public DisconnectableInputStream( InputStream in ){
if( in != null ){
this.in = in;
}else{
throw new NullPointerException( "in" );
}
}
//------------------------------------------------------------------
// java.io.InputStream methods
//------------------------------------------------------------------
// read method
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
// public long skip( long length )
//------------------------------------------------------------------
/**
* 接続された入力ストリームから 次の1バイトのデータを得る。
*
* @return 読み込まれた1バイトのデータ。
* EndOfStreamに達した場合は -1 を返す。
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read() throws IOException {
return this.in.read(); //throws IOException
}
/**
* 接続された入力ストリームから バイト配列 buffer を
* 満たすようにデータを読み込む。
* データは必ずしも buffer を満たすとは限らないことに注意。
*
* @param buffer 読み込まれたデータを格納するためのバイト配列
*
* @return buffer に読み込んだデータ量をバイト数で返す。
* 既に EndOfStream に達していた場合は -1 を返す。
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer ) throws IOException {
return this.in.read( buffer, 0, buffer.length ); //throws IOException
}
/**
* 接続された入力ストリームから バイト配列 buffer の
* index で指定された位置から length バイトのデータを
* 読み込む。
* データは必ずしも length バイト読み込まれるとは限ら
* ないことに注意。
*
* @param buffer 読み込まれたデータを格納するためのバイト配列
* @param index buffer内のデータ読み込み開始位置
* @param length bufferに読み込むデータ量
*
* @return buffer に読み込んだデータ量をバイト数で返す。
* 既に EndOfStream に達していた場合は -1 を返す。
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer, int index, int length ) throws IOException {
if( 0 < length ){
return this.in.read( buffer, index, length ); //throws IOException
}else{
return 0;
}
}
/**
* 接続された入力ストリームのデータを length バイト
* 読み飛ばす。
*
* @param length 読み飛ばすバイト数。
*
* @return 実際に読み飛ばされたバイト数。
*
* @exception IOException 入出力エラーが発生した場合
*/
public long skip( long length ) throws IOException {
if( 0 < length ){
return this.in.skip( length ); //throws IOException
}else{
return 0;
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupprted()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*/
public void mark( int readLimit ){
this.in.mark( readLimit );
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void reset() throws IOException {
this.in.reset(); //throws IOException
}
/**
* 接続された入力ストリームが mark() と reset() を
* サポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public boolean markSupprted(){
return this.in.markSupported();
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* 接続された入力ストリームからブロックしないで
* 読み込むことのできるバイト数を得る。
*
* @return ブロックしないで読み出せるバイト数。
*
* @exception IOException 入出力エラーが発生した場合
*/
public int available() throws IOException {
return this.in.available(); //throws IOException
}
/**
* 接続された入力ストリームとの接続を解除する。
* このメソッドは disconnect() を呼ぶだけである。
*/
public void close(){
this.disconnect();
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.io.Disconnectable
//------------------------------------------------------------------
// public void disconnect
//------------------------------------------------------------------
/**
* 接続された入力ストリームとの接続を解除する。
*/
public void disconnect(){
this.in = null;
}
}
//end of DisconnectableInputStream.java
jp/gr/java_conf/dangan/io/DisconnectableOutputStream.java 100644 0 0 15356 7517367000 21207 0 ustar 0 0 //start of DisconnectableOutputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* DisconnectableOutputStream.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
import java.io.OutputStream;
import jp.gr.java_conf.dangan.io.Disconnectable;
//import exceptions
import java.io.IOException;
import java.lang.NullPointerException;
/**
* データを処理して出力する出力ストリームと
* データをデバイスに出力するストリームとの
* 接続を解除するためのユーティリティクラス。
*
*
* -- revision history --
* $Log: DisconnectableOutputStream.java,v $
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [maintenance]
* タブ廃止
* ライセンス文の修正
* ソース整備
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class DisconnectableOutputStream extends OutputStream
implements Disconnectable {
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private OutputStream out
//------------------------------------------------------------------
/**
* 接続された出力ストリーム
*/
private OutputStream out;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private DisconnectableOutputStream()
// public DisconnectableOutputStream( OutputStream out )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private DisconnectableOutputStream(){ }
/**
* out との接続を解除可能な出力ストリームを構築する。
*
* @param out 出力ストリーム
*/
public DisconnectableOutputStream( OutputStream out ){
if( out != null ){
this.out = out;
}else{
throw new NullPointerException( "out" );
}
}
//------------------------------------------------------------------
// method of java.io.OutputStream method
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* 接続された出力ストリームに 1バイトのデータを出力する。
*
* @param data 書きこまれるべき 1バイトのデータ。
* 一般的に上位3バイトは無視される。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( int data ) throws IOException {
this.out.write( data ); //throws IOException
}
/**
* 接続された出力ストリームに buffer内のデータを
* 全て出力する。
*
* @param buffer 書きこまれるべきデータを格納した
* バイト配列。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer ) throws IOException {
this.out.write( buffer, 0, buffer.length ); //throws IOException
}
/**
* 接続された出力ストリームに buffer内のデータを
* indexで指定された位置から lengthバイト出力する。
*
* @param buffer 書きこまれるべきデータを格納した
* バイト配列。
* @param index buffer内の書きこむべきデータの開始位置。
* @param length 書きこむべきデータ量。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer, int index, int length )
throws IOException {
this.out.write( buffer, index, length ); //throws IOException
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* 接続された出力ストリームに蓄えられたデータを全て出力する
* ように指示する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void flush() throws IOException {
this.out.flush();
}
/**
* 接続された出力ストリームとの接続を解除する。
* このメソッドは disconnect() を呼び出すだけである。
*/
public void close(){
this.disconnect();
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.io.Disconnectable
//------------------------------------------------------------------
// public void disconnect()
//------------------------------------------------------------------
/**
* 接続された出力ストリームとの接続を解除する。
*/
public void disconnect(){
this.out = null;
}
}
//end of DisconnectableOutputStream.java
jp/gr/java_conf/dangan/io/GrowthByteBuffer.java 100644 0 0 31407 7573513400 17117 0 ustar 0 0 //start of GrowthByteBuffer.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* GrowthByteBuffer.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
//import exceptions
import java.lang.IllegalArgumentException;
/**
* 自動的に伸張するバッファ。
* RandomAccessFile の メモリ版として使用する。
* ただし、あまり巨大なデータを取り扱うのには向かない。
* スレッドセーフではない。
* jdk1.4 以降の ByteBufferとは互換性が無い。
*
*
* -- revision history --
* $Log: GrowthByteBuffer.java,v $
* Revision 1.1 2002/12/05 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [bug fix]
* grow() でバッファの増加量の計算が間違っていたのを修正。
* [change]
* 読み込み限界に達した後の read( new byte[0] ) や
* read( byte[] buf, int off, 0 ) の戻り値を
* InputStream と同じく 0 になるようにした
* [maintenance]
* ソース整備
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class GrowthByteBuffer{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// default
//------------------------------------------------------------------
// private static final int DefaultBufferSize
//------------------------------------------------------------------
/**
* デフォルトの一つのバッファのサイズ
*/
private static final int DefaultBufferSize = 16384;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// byte buffer
//------------------------------------------------------------------
// private byte[][] buffer
// private int position
// private int limit
//------------------------------------------------------------------
/**
* バッファ
* 全て buffer[0].length と同じサイズのbyte配列の配列。
*/
private byte[][] buffer;
/**
* 現在処理位置。
* position は limit以降になる可能性もある。
*/
private int position;
/**
* 現在読みこみ限界。
* これ以降のデータは不定。
* この位置のデータは読めることに注意すること。
*/
private int limit;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// public GrowthByteBuffer()
// public GrouthByteBuffer( int BufferSize )
//------------------------------------------------------------------
/**
* サイズを自動で伸張するバッファを構築する。
* バッファサイズにはデフォルト値が使用される。
*/
public GrowthByteBuffer(){
this( GrowthByteBuffer.DefaultBufferSize );
}
/**
* サイズを自動で伸張するバッファを構築する。
*
* @param BufferSize バッファのサイズ
*/
public GrowthByteBuffer( int BufferSize ){
if( 0 < BufferSize ){
this.buffer = new byte[16][];
this.buffer[0] = new byte[ BufferSize ];
this.position = 0;
this.limit = -1;
}else{
throw new IllegalArgumentException( "BufferSize most be 1 or more." );
}
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* 現在位置に 1バイトのデータを書きこむ。
*
* @param data 1バイトのデータ
*/
public void write( int data ){
this.grow( this.position );
this.buffer[ this.position / this.buffer[0].length ]
[ this.position % this.buffer[0].length ]
= (byte)data;
this.position++;
}
/**
* 現在位置に buffer の内容を書きこむ。
*
* @param buffer 書きこむデータほ格納されたバッファ
*/
public void write( byte[] buffer ){
this.write( buffer, 0, buffer.length );
}
/**
* 現在位置に buffer の indexからlengthバイトの内容を書きこむ。
*
* @param buffer 書きこむデータほ格納されたバッファ
* @param index buffer内の書きこむデータの開始位置
* @param length 書きこむデータ量
*/
public void write( byte[] buffer, int index, int length ){
this.grow( this.position + length - 1 );
while( 0 < length ){
int copylen = Math.min( ( this.position / this.buffer[0].length + 1 )
* this.buffer[0].length,
this.position + length ) - this.position;
System.arraycopy( buffer, index,
this.buffer[ this.position / this.buffer[0].length ],
this.position % this.buffer[0].length,
copylen );
this.position += copylen;
index += copylen;
length -= copylen;
}
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* 現在位置から 1byteのデータを読みこむ。
*
* @return 読みこまれた1byteのデータ。
* 読みこみ限界を超えて読もうとした場合は -1
*/
public int read(){
if( this.position <= this.limit ){
return this.buffer[ this.position / this.buffer[0].length ]
[ this.position++ % this.buffer[0].length ] & 0xFF;
}else{
return -1;
}
}
/**
* 現在位置から bufferを満たすようにデータを読み込む。
*
* @param buffer データを読み込むバッファ
*
* @return 実際に読みこまれたデータ量
* 読みこみ限界を超えて読もうとした場合は -1
*/
public int read( byte[] buffer ){
return this.read( buffer, 0, buffer.length );
}
/**
* 現在位置から buffer のindexへ lengthのデータを読み込む。
*
* @param buffer データを読み込むバッファ
* @param index buffer内データ読みこみ位置
* @param length 読み込むデータの量
*
* @return 実際に読みこまれたデータ量
* 読みこみ限界を超えて読もうとした場合は -1
*/
public int read( byte[] buffer, int index, int length ){
if( this.position <= this.limit ){
int len = 0;
while( 0 < length ){
int copylen = Math.min( Math.min( ( this.position / this.buffer[0].length + 1 )
* this.buffer[0].length,
this.position + length ),
this.limit + 1 ) - this.position;
if( 0 < copylen ){
System.arraycopy( this.buffer[ this.position / this.buffer[0].length ],
this.position % this.buffer[0].length,
buffer, index,
copylen );
this.position += copylen;
index += copylen;
len += copylen;
length -= copylen;
}else{
break;
}
}
return len;
}else if( 0 < length ){
return -1;
}else{
return 0;
}
}
//------------------------------------------------------------------
// original methods
//------------------------------------------------------------------
// access methods
//------------------------------------------------------------------
// public int length()
// public void setLength( int length )
// public int position()
// public void setPosition( int position )
// public void seek( int position )
//------------------------------------------------------------------
/**
* 現在の読みこみ限界を得る。
*
* @return 現在の読みこみ限界
*/
public int length(){
return this.limit + 1;
}
/**
* 読みこみ限界位置を設定する。
*
* @param 新しい読みこみ限界位置
*/
public void setLength( int length ){
length--;
if( this.limit < length ){
this.grow( length );
}else{
this.limit = length;
}
}
/**
* 現在位置を得る。
*
* @return 現在位置
*/
public int position(){
return this.position;
}
/**
* 現在位置を設定する。
* java.io.RandomAccessFileと同じく
* setPosition で読みこみ限界を超えた値を
* 設定した直後にはバッファは増加していない。
* その後 write によって書きこんだ時にはじ
* めてバッファは増加する。
*
* @param position 新しい現在位置
*/
public void setPosition( int position ){
this.position = position;
}
/**
* 現在位置を設定する。
* java.io.RandomAccessFileと同じく
* seek で読みこみ限界を超えた値を
* 設定した直後にはバッファは増加していない。
* その後 write によって書きこんだ時にはじ
* めてバッファは増加する。
*
* @param position 新しい現在位置
*/
public void seek( int position ){
this.setPosition( position );
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private void grow( int limit )
//------------------------------------------------------------------
/**
* 新しい読みこみ限界 limit を設定し、
* limit まで バッファを増加させる。
*
* @param 新しい読みこみ限界
*/
private void grow( int limit ){
if( this.limit < limit ){
int last = 0;
while( last < this.buffer.length
&& this.buffer[last] != null )
last++;
limit++;
if( last * this.buffer[0].length < limit ){
int need = ( limit / this.buffer[0].length )
+ ( limit % this.buffer[0].length == 0 ? 0 : 1 );
if( this.buffer.length < need ){
byte[][] old = this.buffer;
this.buffer = new byte[ Math.max( old.length * 2, need ) ][];
for( int i = 0 ; i < last ; i++ )
this.buffer[i] = old[i];
}
for( int i = last ; i < need ; i++ )
this.buffer[ i ] = new byte[ this.buffer[0].length ];
}
this.limit = limit - 1;
}
}
}
//end of GrowthByteBuffer.java
jp/gr/java_conf/dangan/io/LimitedInputStream.java 100644 0 0 30726 7706545551 17467 0 ustar 0 0 //start of LimitedInputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LimitedInputStream.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
import java.io.InputStream;
import java.lang.Math;
//import exceptions
import java.io.IOException;
import java.lang.IllegalArgumentException;
/**
* 読み込み可能なデータ量が制限された入力ストリーム。
*
*
* -- revision history --
* $Log: LimitedInputStream.java,v $
* Revision 1.1.2.1 2003/07/20 17:03:37 dangan
* [maintenance]
* 最新の LimitedInputStream からソースを取り込む。
*
* Revision 1.1 2002/12/05 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [change]
* EndOfStream に達した後の read( new byte[0] ) や
* read( byte[] buf, int off, 0 ) の戻り値を
* InputStream と同じく 0 になるようにした
* [maintenance]
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1.2.1 $
*/
public class LimitedInputStream extends InputStream{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private InputStream in
//------------------------------------------------------------------
/**
* 接続された入力ストリーム
*/
private InputStream in;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// current position
//------------------------------------------------------------------
// private long position
// private final long limit
// private long markPosition
//------------------------------------------------------------------
/**
* 現在読み込み位置
*/
private long position;
/**
* 読み込み限界
*/
private final long limit;
/**
* マーク位置
*/
private long markPosition;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public LimitedInputStream( InputStream in, long limit )
//------------------------------------------------------------------
/**
* in からの読み込み可能なデータ量を制限した
* 入力ストリームを構築する。
*
* @param in 入力ストリーム
* @param limit 読み込み可能バイト数
*
* @exception IllegalArgumentException
* limit が負数である場合
*/
public LimitedInputStream( InputStream in, long limit ){
if( in != null && 0 <= limit ){
this.in = in;
this.position = 0;
this.limit = limit;
this.markPosition = -1;
}else if( in == null ){
throw new NullPointerException( "in" );
}else{
throw new IllegalArgumentException( "limit must be 0 or more." );
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
// public long skip( long length )
//------------------------------------------------------------------
/**
* 接続された入力ストリームから 1バイトのデータを読み込む。
*
* @return 読み込まれた 1バイトのデータ
* 既にEndOfStream に達していたか、
* 制限に達した場合は -1 を返す。
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read() throws IOException {
if( this.position < this.limit ){
int ret = this.in.read(); //throws IOException
if( 0 <= ret ){
this.position++;
}
return ret;
}else{
return -1;
}
}
/**
* 接続された入力ストリームから buffer を満たすように
* データを読み込む。
* データは必ずしも buffer を満たすとは限らないことに注意。
*
* @param buffer 読み込んだデータを格納するためのバイト配列
*
* @return buffer に読み込んだデータ量をバイト数で返す。
* 既にEndOfStream に達していたか、
* 制限に達した場合は -1 を返す。
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer ) throws IOException {
if( 0 < buffer.length ){
int ret;
if( buffer.length < this.limit - this.position ){
ret = this.in.read( buffer ); //throws IOException
}else if( this.position < this.limit ){
ret = this.in.read( buffer, 0, (int)( this.limit - this.position ) );//throws IOException
}else{
return -1;
}
if( 0 < ret ){
this.position += ret;
}
return ret;
}else{
return 0;
}
}
/**
* 接続された入力ストリームから バイト配列 buffer の
* index で指定された位置から length バイトのデータを
* 読み込む。
* データは必ずしも length バイト読み込まれるとは限ら
* ないことに注意。
*
* @param buffer 読み込まれたデータを格納するためのバイト配列
* @param index buffer内のデータ読み込み開始位置
* @param length bufferに読み込むデータ量
*
* @return buffer に読み込んだデータ量をバイト数で返す。
* 既にEndOfStream に達していたか、
* 制限に達した場合は -1 を返す。
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer, int index, int length )
throws IOException {
if( 0 < length ){
if( this.limit <= this.position ){
return -1;
}else if( this.limit - this.position < length ){
length = (int)( this.limit - this.position );
}
int ret = this.in.read( buffer, index, length ); //throws IOException
if( 0 < ret ){
this.position += ret;
}
return ret;
}else{
return 0;
}
}
/**
* 接続された入力ストリームのデータを length バイト読み飛ばす。
*
* @param length 読み飛ばすバイト数。
*
* @return 実際に読み飛ばされたバイト数。
*
* @exception IOException 入出力エラーが発生した場合
*/
public long skip( long length ) throws IOException {
if( 0 < length ){
if( this.limit <= this.position ){
return 0;
}else if( this.limit - this.position < length ){
length = this.limit - this.position;
}
length = this.in.skip( length ); //throws IOException
if( 0 < length ){
this.position += length;
}
return length;
}else{
return 0;
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupprted()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*/
public void mark( int readLimit ){
this.in.mark( readLimit );
this.markPosition = this.position;
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException
*
* - LimitedInputStream に mark がなされていない場合。
* - 接続された入力ストリームが markSupported()で
* false を返す場合。
* - 接続された入力ストリームで
* 入出力エラーが発生した場合。
*
* の何れか。
*/
public void reset() throws IOException {
if( !this.in.markSupported() ){
throw new IOException( "not support mark()/reset()." );
}else if( this.markPosition < 0 ){ //コンストラクタで MarkPosition が -1 に設定されるのを利用する。
throw new IOException( "not marked." );
}else{
this.in.reset(); //throws IOException
this.position = this.markPosition;
}
}
/**
* 接続された入力ストリームが mark() と reset() を
* サポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public boolean markSupported(){
return this.in.markSupported();
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* 接続された入力ストリームからブロックしないで
* 読み込むことのできるバイト数を得る。
*
* @return ブロックしないで読み出せるバイト数。
*
* @exception IOException 入出力エラーが発生した場合
*/
public int available() throws IOException {
return (int)Math.min( (long)this.in.available(), //throws IOException
this.limit - this.position );
}
/**
* この入力ストリームを閉じ、使用していた
* 全てのリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.in.close(); //throws IOException
this.in = null;
}
}
//end of LimitedInputStream.java
jp/gr/java_conf/dangan/io/LittleEndian.java 100644 0 0 33507 7517367000 16247 0 ustar 0 0 //start of LittleEndian.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LittleEndian.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
import java.io.InputStream;
import java.io.OutputStream;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.lang.ArrayIndexOutOfBoundsException;
/**
* リトルエンディアンで
* バイト配列や InputStream, OutputStream
* にアクセスするメソッドを提供するユーティリティクラス。
*
*
* -- revision history --
* $Log: LittleEndian.java,v $
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [change]
* writeByte(), readByte() を撤去。
* [maintenance]
* ソース整備
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class LittleEndian{
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private LittleEndian()
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。使用不可。
*/
private LittleEndian(){ }
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// read from byte array
//------------------------------------------------------------------
// public static final int readShort( byte[] ByteArray, int index )
// public static final int readInt( byte[] ByteArray, int index )
// public static final long readLong( byte[] ByteArray, int index )
//------------------------------------------------------------------
/**
* ByteArray の index の位置から リトルエンディアンで
* 2バイト値を読み出す。読み出された 2バイト値は
* 0x0000〜0xFFFFにマップされる。
*
* @param ByteArray バイト配列
* @param index ByteArray内のデータの開始位置
*
* @return 読み出された2バイト値
*
* @exception ArrayIndexOutOfBoundsException
* indexから始まるデータが
* ByteArrayの範囲内に無い場合。
*/
public static final int readShort( byte[] ByteArray, int index ){
return ( ByteArray[index] & 0xFF )
| ( ( ByteArray[index + 1] & 0xFF ) << 8 );
}
/**
* ByteArray の index の位置からリトルエンディアンで
* 4バイト値を読み出す。
*
* @param ByteArray バイト配列
* @param index ByteArray内のデータの開始位置
*
* @return 読み出された4バイト値
*
* @exception ArrayIndexOutOfBoundsException
* indexから始まるデータが
* ByteArrayの範囲内に無い場合。
*/
public static final int readInt( byte[] ByteArray, int index ){
return ( ByteArray[index] & 0xFF )
| ( ( ByteArray[index + 1] & 0xFF ) << 8 )
| ( ( ByteArray[index + 2] & 0xFF ) << 16 )
| ( ByteArray[index + 3] << 24 );
}
/**
* ByteArray の index の位置からリトルエンディアンで
* 8バイト値を読み出す。
*
* @param ByteArray バイト配列
* @param index ByteArray内のデータの開始位置
*
* @return 読み出された8バイト値
*
* @exception ArrayIndexOutOfBoundsException
* indexから始まるデータが
* ByteArrayの範囲内に無い場合。
*/
public static final long readLong( byte[] ByteArray, int index ){
return ( (long)LittleEndian.readInt( ByteArray, index ) & 0xFFFFFFFFL )
| ( (long)LittleEndian.readInt( ByteArray, index + 4 ) << 32L );
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// read from InputStream
//------------------------------------------------------------------
// public static final int readShort( InputStream in )
// public static final int readInt( InputStream in )
// public static final long readLong( InputStream in )
//------------------------------------------------------------------
/**
* 入力ストリーム in から リトルエンディアンで
* 2byte値を読み出す。
*
* @param in 入力ストリーム
*
* @return 読み出された2byte値
*
* @exception EOFException
* 既に End Of Streamに達していたか、
* 読み込みの途中で End Of Streamに達した。
* 読み込み途中のデータは消失する。
* @exception IOException
* 入出力エラーが発生した場合
*/
public static final int readShort( InputStream in )
throws IOException {
int byte1 = in.read();
int byte2 = in.read();
if( 0 <= byte1 && 0 <= byte2 ){
return ( byte1 & 0xFF )
| ( ( byte2 & 0xFF ) << 8 );
}else{
throw new EOFException();
}
}
/**
* 入力ストリーム in から リトルエンディアンで
* 4byte値を読み出す。
*
* @param in 入力ストリーム
*
* @return 読み出された4byte値
*
* @exception EOFException
* 既に End Of Streamに達していたか、
* 読み込みの途中で End Of Streamに達した。
* 読み込み途中のデータは消失する。
* @exception IOException
* 入出力エラーが発生した場合
*/
public static final int readInt( InputStream in )
throws IOException {
int byte1 = in.read();
int byte2 = in.read();
int byte3 = in.read();
int byte4 = in.read();
if( 0 <= byte1 && 0 <= byte2 && 0 <= byte3 && 0 <= byte4 ){
return ( byte1 & 0xFF )
| ( ( byte2 & 0xFF ) << 8 )
| ( ( byte3 & 0xFF ) << 16 )
| ( byte4 << 24 );
}else{
throw new EOFException();
}
}
/**
* 入力ストリーム in から リトルエンディアンで
* 8byte値を読み出す。
*
* @param in 入力ストリーム
*
* @return 読み出された8byte値
*
* @exception EOFException
* 既に End Of Streamに達していたか、
* 読み込みの途中で End Of Streamに達した。
* 読み込み途中のデータは消失する。
* @exception IOException
* 入出力エラーが発生した場合
*/
public static final long readLong( InputStream in )
throws IOException {
return ( (long)LittleEndian.readInt( in ) & 0xFFFFFFFFL )
| ( (long)LittleEndian.readInt( in ) << 32 );
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// write to byte array
//------------------------------------------------------------------
// public static final void writeShort( byte[] ByteArray, int index, int value )
// public static final void writeInt( byte[] ByteArray, int index, int value )
// public static final void writeLong( byte[] ByteArray, int index, long value )
//------------------------------------------------------------------
/**
* ByteArray の index の位置にリトルエンディアンで
* 2byte値を書き出す。
*
* @param ByteArray バイト配列
* @param index ByteArray内のデータを書きこむ位置
* @param value 書きこむ 2byte値
*
* @exception ArrayIndexOutOfBoundsException
* indexから始まるデータが
* ByteArrayの範囲内に無い場合。
*/
public static final void writeShort( byte[] ByteArray,
int index,
int value ){
if( 0 <= index && index + 1 < ByteArray.length ){
ByteArray[index] = (byte)value;
ByteArray[index + 1] = (byte)( value >> 8 );
}else{
throw new ArrayIndexOutOfBoundsException();
}
}
/**
* ByteArray の index の位置にリトルエンディアンで
* 4byte値を書き出す。
*
* @param ByteArray バイト配列
* @param index ByteArray内のデータを書きこむ位置
* @param value 書きこむ 4byte値
*
* @exception ArrayIndexOutOfBoundsException
* indexから始まるデータが
* ByteArrayの範囲内に無い場合。
*/
public static final void writeInt( byte[] ByteArray,
int index,
int value ){
if( 0 <= index && index + 3 < ByteArray.length ){
ByteArray[index] = (byte)value;
ByteArray[index + 1] = (byte)( value >> 8 );
ByteArray[index + 2] = (byte)( value >> 16 );
ByteArray[index + 3] = (byte)( value >> 24 );
}else{
throw new ArrayIndexOutOfBoundsException();
}
}
/**
* ByteArray の index の位置にリトルエンディアンで
* 8byte値を書き出す。
*
* @param ByteArray バイト配列
* @param index ByteArray内のデータを書きこむ位置
* @param value 書きこむ 8byte値
*
* @exception ArrayIndexOutOfBoundsException
* indexから始まるデータが
* ByteArrayの範囲内に無い場合。
*/
public static final void writeLong( byte[] ByteArray,
int index,
long value ){
if( 0 <= index && index + 7 < ByteArray.length ){
LittleEndian.writeInt( ByteArray, index, (int)value );
LittleEndian.writeInt( ByteArray, index + 4, (int)(value >> 32) );
}else{
throw new ArrayIndexOutOfBoundsException();
}
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// write to OutputStream
//------------------------------------------------------------------
// public static final void writeShort( OutputStream out, int value )
// public static final void writeInt( OutputStream out, int value )
// public static final void writeLong( OutputStream out, long value )
//------------------------------------------------------------------
/**
* 出力ストリーム out に リトルエンディアンで
* 2バイト書き出す。
*
* @param out 出力ストリーム
* @param value 書き出す2バイト値
*
* @exception IOException
* 入出力エラーが発生した場合
*/
public static final void writeShort( OutputStream out, int value )
throws IOException {
out.write( value & 0xFF );
out.write( ( value >> 8 ) & 0xFF );
}
/**
* 出力ストリーム out に リトルエンディアンで
* 4バイト値を書き出す。
*
* @param out 出力ストリーム
* @param value 書き出す1バイト値
*
* @exception IOException
* 入出力エラーが発生した場合
*/
public static final void writeInt( OutputStream out, int value )
throws IOException {
out.write( value & 0xFF );
out.write( ( value >> 8 ) & 0xFF );
out.write( ( value >> 16 ) & 0xFF );
out.write( value >>> 24 );
}
/**
* 出力ストリーム out に リトルエンディアンで
* 8バイト値を書き出す。
*
* @param out 出力ストリーム
* @param value 書き出す1バイト値
*
* @exception IOException
* 入出力エラーが発生した場合
*/
public static final void writeLong( OutputStream out, long value )
throws IOException {
int low = (int)value;
int hi = (int)( value >> 32 );
out.write( low & 0xFF );
out.write( ( low >> 8 ) & 0xFF );
out.write( ( low >> 16 ) & 0xFF );
out.write( low >>> 24 );
out.write( hi & 0xFF );
out.write( ( hi >> 8 ) & 0xFF );
out.write( ( hi >> 16 ) & 0xFF );
out.write( hi >>> 24 );
}
}
//end of LittleEndian.java
jp/gr/java_conf/dangan/io/NotEnoughBitsException.java 100644 0 0 10267 7517367000 20300 0 ustar 0 0 //start of NotEnoughBitsException.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* NotEnoughBitsException.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.io;
//import classes and interfaces
//import exceptions
import java.io.IOException;
/**
* 要求されたビット数のデータを得られなかった場合に
* 投げられる例外。
* BitDataBrokenException と違い、こちらの例外を
* 投げる場合には 実際には読み込み動作を行ってい
* ないため、読み込み位置は例外を投げる前の時点と
* 同じである点に注意すること。
*
*
* -- revision history --
* $Log: NotEnoughBitsException.java,v $
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [maintenance]
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class NotEnoughBitsException extends IOException{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private int availableBits
//------------------------------------------------------------------
/**
* 実際に読み込めるビット数
*/
private int availableBits;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private NotEnoughBitsException()
// public NotEnoughBitsException( int availableBits )
// public NotEnoughBitsException( String message, int availableBits )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private NotEnoughBitsException(){ }
/**
* availableBits 使用可能であることを示す
* NotEnoughBitsException を構築する。
*
* @param availableBits 使用可能なビット数
*/
public NotEnoughBitsException( int availableBits ){
super();
this.availableBits = availableBits;
}
/**
* availableBits 使用可能であることを示し、
* 詳細なメッセージを持つ
* NotEnoughBitsException を構築する。
*
* @param message 詳細なメッセージ
* @param availableBits 使用可能なビット数
*/
public NotEnoughBitsException( String message, int availableBits ){
super( message );
this.availableBits = availableBits;
}
//------------------------------------------------------------------
// access method
//------------------------------------------------------------------
// public int getAvailableBits()
//------------------------------------------------------------------
/**
* 使用可能なビット数を得る。
* この例外を投げたメソッドにおいて、現在使用可能なビット数を返す。
*
* @return 使用可能なビット数
*/
public int getAvailableBits(){
return this.availableBits;
}
}
//end of NotEnoughBitsException.java
jp/gr/java_conf/dangan/lang/reflect/Factory.java 100644 0 0 42056 7546162600 17240 0 ustar 0 0 //start of Factory.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* Factory.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.lang.reflect;
//import classes and interfaces
import java.lang.reflect.Constructor;
import java.text.NumberFormat;
//import exceptions
import java.lang.NoSuchMethodException;
import java.lang.ClassNotFoundException;
import java.lang.InstantiationException;
import java.lang.reflect.InvocationTargetException;
/**
* クラス名と 引数になるObject の配列から、
* createInstance() によって新しいインスタンスを作り出す
* ユーティリティクラス。
*
*
* -- revision history --
* $Log: Factory.java,v $
* Revision 1.0 2002/10/01 00:00:00 dangan
* first edition
* add to version control
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class Factory{
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private Factory()
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private Factory(){ }
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// create instance
//------------------------------------------------------------------
// public static Object createInstance( String classname, Object[] args )
// public static Object createInstance( Class clas, Object[] args )
//------------------------------------------------------------------
/**
* classname で示されるクラスのインスタンスを生成する。
* コンストラクタには args の型と一致するものを使用する。
*
* @param classname クラス名
* @param args 引数の配列
*
* @return 生成されたインスタンス
* args と型情報がマッチする
* コンストラクタが存在しなかった場合は null
*
* @exception InvocationTargetException
* コンストラクタで例外が発生した場合
*
* @exception InstantiationException
* abstractクラスのインスタンスを得ようとした場合
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Object createInstance( String classname, Object[] args )
throws InvocationTargetException,
InstantiationException,
ClassNotFoundException,
NoSuchMethodException {
return Factory.createInstance( Class.forName( classname ), args );
}
/**
* type で示されるクラスのインスタンスを生成する。
* コンストラクタには args の型と一致するものを使用する。
*
* @param type クラス名
* @param args 引数の配列
*
* @return 生成されたインスタンス
* args と型情報がマッチする
* コンストラクタが存在しなかった場合は null
*
* @exception InvocationTargetException
* コンストラクタで例外が発生した場合
*
* @exception InstantiationException
* abstractクラスのインスタンスを得ようとした場合
*/
public static Object createInstance( Class type, Object[] args )
throws InvocationTargetException,
InstantiationException,
NoSuchMethodException {
Constructor constructor = Factory.getMatchFullConstructor( type, args );
if( constructor == null ){
constructor = Factory.getConstructor( type, args );
if( constructor != null )
args = Type.parseAll( constructor.getParameterTypes(), args );
}
if( constructor != null ){
try{
return constructor.newInstance( args );
}catch( IllegalAccessException exception ){
throw new IllegalAccessError( exception.toString() );
}
}else{
throw new NoSuchMethodException();
}
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// get constructor
//------------------------------------------------------------------
// public static Constructor getConstructor( String classname,
// Object[] args )
// public static Constructor getConstructor( Class type, Object[] args )
// public static Constructor getConstructor( String classname,
// Object[] args, boolean all )
// public static Constructor getConstructor( Class type,
// Object[] args, boolean all )
//------------------------------------------------------------------
/**
* classname で示されるクラスの public なコンストラクタのうち、
* args を Type.parse した場合、引数として受け入れることが
* できるものを得る。
*
* @param classname クラス名。
* この型のインスタンスを生成するための
* コンストラクタを得る。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のコンストラクタ以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができるコンストラクタ。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Constructor getConstructor( String classname,
Object[] args )
throws ClassNotFoundException {
return Factory.getConstructor( Class.forName( classname ),
args );
}
/**
* type のpublic なコンストラクタのうち、args を
* Type.parse した場合 引数として受け入れることができるものを得る。
*
* @param type 型情報。
* この型のインスタンスを生成するためのコンストラクタを得る。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のコンストラクタ以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができるコンストラクタ。
* 見つからなければ null。
*/
public static Constructor getConstructor( Class type,
Object[] args ){
return Factory.getConstructor( type, args, false );
}
/**
* classnameで示されるクラスの コンストラクタのうち、args を
* Type.parse して 引数として受け入れることができるものを得る。
*
* @param classname クラス名。
* この型のインスタンスを生成するためのコンストラクタを得る。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のコンストラクタ以外のものが見つかる可能性がある。
* @param all public のコンストラクタのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのコンストラクタをけんさくするなら true。
*
* @return args を引数に取ることができるコンストラクタ。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Constructor getConstructor( String classname,
Object[] args,
boolean all )
throws ClassNotFoundException {
return Factory.getConstructor( Class.forName( classname ),
args,
all );
}
/**
* type のコンストラクタのうち、args を Type.parse した場合
* 引数として受け入れることができるものを得る。
*
* @param type 型情報。
* この型のインスタンスを生成するためのコンストラクタを得る。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のコンストラクタ以外のものが見つかる可能性がある。
* @param all public のコンストラクタのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのコンストラクタをけんさくするなら true。
*
* @return args を引数に取ることができるコンストラクタ。
* 見つからなければ null。
*/
public static Constructor getConstructor( Class type,
Object[] args,
boolean all ){
Constructor[] constructors = all
? type.getDeclaredConstructors()
: type.getConstructors();
for( int i = 0 ; i < constructors.length ; i++ )
if( Type.matchFullAll( constructors[i].getParameterTypes(), args ) )
return constructors[i];
for( int i = 0 ; i < constructors.length ; i++ )
if( Type.matchRestrictAll( constructors[i].getParameterTypes(), args ) )
return constructors[i];
for( int i = 0 ; i < constructors.length ; i++ )
if( Type.matchAll( constructors[i].getParameterTypes(), args ) )
return constructors[i];
return null;
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// get match full constructor
//------------------------------------------------------------------
// public static Constructor getMatchFullConstructor( String classname,
// Object[] args )
// public static Constructor getMatchFullConstructor( Class type,
// Object[] args )
// public static Constructor getMatchFullConstructor( String classname,
// Object[] args, boolean all )
// public static Constructor getMatchFullConstructor( Class type,
// Object[] args, boolean all )
//------------------------------------------------------------------
/**
* classname で示されるクラスの public なコンストラクタのうち、
* args を そのまま引数として受け入れることができるものを得る。
*
* @param classname クラス名。
* この型のインスタンスを生成するための
* コンストラクタを得る。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のコンストラクタ以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができるコンストラクタ。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Constructor getMatchFullConstructor( String classname,
Object[] args )
throws ClassNotFoundException {
return Factory.getMatchFullConstructor( Class.forName( classname ),
args );
}
/**
* type の public なコンストラクタのうち、args を
* そのまま引数として受け入れることができるものを得る。
*
* @param type 型情報。
* この型のインスタンスを生成するためのコンストラクタを得る。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のコンストラクタ以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができるコンストラクタ。
* 見つからなければ null。
*/
public static Constructor getMatchFullConstructor( Class type,
Object[] args ){
return Factory.getMatchFullConstructor( type, args, false );
}
/**
* classname で示されるクラスの コンストラクタのうち、
* args を そのまま引数として受け入れることができるものを得る。
*
* @param classname クラス名。
* この型のインスタンスを生成するための
* コンストラクタを得る。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のコンストラクタ以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができるコンストラクタ。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Constructor getMatchFullConstructor( String classname,
Object[] args,
boolean all )
throws ClassNotFoundException {
return Factory.getMatchFullConstructor( Class.forName( classname ),
args,
all );
}
/**
* type のコンストラクタのうち、args を
* そのまま引数として受け入れることができるものを得る。
*
* @param type 型情報。
* この型のインスタンスを生成するためのコンストラクタを得る。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のコンストラクタ以外のものが見つかる可能性がある。
* @param all public のコンストラクタのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのコンストラクタをけんさくするなら true。
*
* @return args を引数に取ることができるコンストラクタ。
* 見つからなければ null。
*/
public static Constructor getMatchFullConstructor( Class type,
Object[] args,
boolean all ){
Constructor[] constructors = all
? type.getDeclaredConstructors()
: type.getConstructors();
for( int i = 0 ; i < constructors.length ; i++ )
if( Type.matchFullAll( constructors[i].getParameterTypes(), args ) )
return constructors[i];
return null;
}
}
//end of Factory.java
jp/gr/java_conf/dangan/lang/reflect/MethodUtil.java 100644 0 0 101077 7546162600 17726 0 ustar 0 0 //start of MethodUtil.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* MethodUtil.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.lang.reflect;
//import classes and interfaces
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
//import exceptions
import java.lang.ClassNotFoundException;
import java.lang.NoSuchMethodException;
import java.lang.reflect.InvocationTargetException;
/**
* メソッドに関するユーティリティクラス。
*
*
* -- revision history --
* $Log: MethodUtil.java,v $
* Revision 1.0 2002/10/01 00:00:00 dangan
* first edition
* add to version control
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class MethodUtil{
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private MethodUtil()
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private MethodUtil(){ }
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// invoke static method
//------------------------------------------------------------------
// public static Object invoke( Object obj, String name, Object[] args )
//------------------------------------------------------------------
/**
* インスタンスobjの nameという名前の
* メソッドをargsを引数として実行する。
*
* @param obj インスタンス
* @param name メソッド名
* @param args 引数の配列
*
* @return 戻り値
*
* @exception InvocationTargetException
* コンストラクタで例外が発生した場合
*
* @exception NoSuchMethodException
* args を引数に取れる name という名前の
* インスタンスメソッドが見つからなかった場合。
*/
public static Object invoke( Object obj, String name, Object[] args )
throws InvocationTargetException,
NoSuchMethodException {
Class type = obj.getClass();
Method method = MethodUtil.getMatchFullInstanceMethod( type, name, args );
if( method == null ){
method = MethodUtil.getInstanceMethod( type, name, args );
if( method != null )
args = Type.parseAll( method.getParameterTypes(), args );
}
if( method != null ){
try{
return method.invoke( obj, args );
}catch( IllegalAccessException exception ){
throw new IllegalAccessError( exception.toString() );
}
}else{
throw new NoSuchMethodException();
}
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// invoke static method
//------------------------------------------------------------------
// public static Object invokeStatic( String classname, String name, Object[] args )
// public static Object invokeStatic( Class type, String name, Object[] args )
//------------------------------------------------------------------
/**
* classname で示されるクラスの nameという名前の
* static メソッドをargsを引数として実行する。
*
* @param classname クラス名
* @param name メソッド名
* @param args 引数の配列
*
* @return 戻り値
*
* @exception ClassNotFoundException
* classname のクラスが見つからなかった場合
*
* @exception InvocationTargetException
* コンストラクタで例外が発生した場合
*
* @exception NoSuchMethodException
* args を引数に取れる name という名前の
* インスタンスメソッドが見つからなかった場合。
*/
public static Object invokeStatic( String classname,
String name,
Object[] args )
throws ClassNotFoundException,
InvocationTargetException,
NoSuchMethodException {
return MethodUtil.invokeStatic( Class.forName( classname ), //throw ClassNotFoundException
name,
args ); //throw InvocationTargetException, NoSuchMethodException
}
/**
* type で示されるクラスの nameという名前の
* static メソッドをargsを引数として実行する。
*
* @param type 型情報
* @param name メソッド名
* @param args 引数の配列
*
* @return 戻り値
*
* @exception InvocationTargetException
* コンストラクタで例外が発生した場合
*
* @exception NoSuchMethodException
* args を引数に取れる name という名前の
* インスタンスメソッドが見つからなかった場合。
*/
public static Object invokeStatic( Class type, String name, Object[] args )
throws InvocationTargetException,
NoSuchMethodException {
Method method = MethodUtil.getMatchFullStaticMethod( type, name, args );
if( method == null ){
method = MethodUtil.getStaticMethod( type, name, args );
if( method != null )
args = Type.parseAll( method.getParameterTypes(), args );
}
if( method != null ){
try{
return method.invoke( null, args );
}catch( IllegalAccessException exception ){
throw new IllegalAccessError( exception.toString() );
}
}else{
throw new NoSuchMethodException();
}
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// get instance method
//------------------------------------------------------------------
// public static Method getInstanceMethod( String classname,
// String name, Object[] args )
// public static Method getInstanceMethod( Class type,
// String name, Object[] args )
// public static Method getInstanceMethod( String classname, String name,
// Object[] args, boolean all )
// public static Method getInstanceMethod( Class type, String name,
// Object[] args, boolean all )
//------------------------------------------------------------------
/**
* classname で示されるクラスの public なインスタンスメソッドのうち、
* name という名前で args を Type.parse した後
* 受け入れることができるものを得る。
*
* @param classname クラス名。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Method getInstanceMethod( String classname,
String name,
Object[] args )
throws ClassNotFoundException {
return MethodUtil.getInstanceMethod( Class.forName( classname ),
name,
args,
false );
}
/**
* type の public なインスタンスメソッドのうち、
* name という名前で args を Type.parse した後
* 受け入れることができるものを得る。
*
* @param type 型情報。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*/
public static Method getInstanceMethod( Class type,
String name,
Object[] args ){
return MethodUtil.getInstanceMethod( type, name, args, false );
}
/**
* classname で示されるクラスの インスタンスメソッドのうち、
* name という名前で args を Type.parse した後
* 受け入れることができるものを得る。
*
* @param classname クラス名。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
* @param all public のメソッドのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのメソッドを検索するなら true。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Method getInstanceMethod( String classname,
String name,
Object[] args,
boolean all )
throws ClassNotFoundException {
return MethodUtil.getInstanceMethod( Class.forName( classname ),
name,
args,
all );
}
/**
* type の インスタンスメソッドのうち、name という名前で args を
* Type.parse した後 受け入れることができるものを得る。
*
* @param type 型情報。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
* @param all public のメソッドのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのメソッドを検索するなら true。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*/
public static Method getInstanceMethod( Class type,
String name,
Object[] args,
boolean all ){
Method[] methods = all
? type.getDeclaredMethods()
: type.getMethods();
for( int i = 0 ; i < methods.length ; i++ )
if( methods[i].getName().equals( name )
&& !Modifier.isStatic( methods[i].getModifiers() )
&& Type.matchFullAll( methods[i].getParameterTypes(), args ) )
return methods[i];
for( int i = 0 ; i < methods.length ; i++ )
if( methods[i].getName().equals( name )
&& !Modifier.isStatic( methods[i].getModifiers() )
&& Type.matchRestrictAll( methods[i].getParameterTypes(), args ) )
return methods[i];
for( int i = 0 ; i < methods.length ; i++ )
if( methods[i].getName().equals( name )
&& !Modifier.isStatic( methods[i].getModifiers() )
&& Type.matchAll( methods[i].getParameterTypes(), args ) )
return methods[i];
return null;
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// get match full instance method
//------------------------------------------------------------------
// public static Method getMatchFullInstanceMethod( String classname,
// String name, Object[] args )
// public static Method getMatchFullInstanceMethod( Class type,
// String name, Object[] args )
// public static Method getMatchFullInstanceMethod( String classname,
// String name, Object[] args, boolean all )
// public static Method getMatchFullInstanceMethod( Class type,
// String name, Object[] args, boolean all )
//------------------------------------------------------------------
/**
* classname で示されるクラスの public なインスタンスメソッドのうち、
* name という名前で args を 直接受け入れることができるものを得る。
*
* @param classname クラス名。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Method getMatchFullInstanceMethod( String classname,
String name,
Object[] args )
throws ClassNotFoundException {
return MethodUtil.getMatchFullInstanceMethod(
Class.forName( classname ),
name,
args,
false );
}
/**
* type の public なインスタンスメソッドのうち、
* name という名前で args を直接受け入れることができるものを得る。
*
* @param type 型情報。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*/
public static Method getMatchFullInstanceMethod( Class type,
String name,
Object[] args ){
return MethodUtil.getMatchFullInstanceMethod( type, name, args, false );
}
/**
* classname で示されるクラスの インスタンスメソッドのうち、
* name という名前で args を直接受け入れることができるものを得る。
*
* @param classname クラス名。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
* @param all public のメソッドのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのメソッドを検索するなら true。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Method getMatchFullInstanceMethod( String classname,
String name,
Object[] args,
boolean all )
throws ClassNotFoundException {
return MethodUtil.getMatchFullInstanceMethod(
Class.forName( classname ),
name,
args,
all );
}
/**
* type の インスタンスメソッドのうち、name という名前で
* args を直接受け入れることができるものを得る。
*
* @param type 型情報。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
* @param all public のメソッドのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのメソッドを検索するなら true。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*/
public static Method getMatchFullInstanceMethod( Class type,
String name,
Object[] args,
boolean all ){
Method[] methods = all
? type.getDeclaredMethods()
: type.getMethods();
for( int i = 0 ; i < methods.length ; i++ )
if( methods[i].getName().equals( name )
&& !Modifier.isStatic( methods[i].getModifiers() )
&& Type.matchFullAll( methods[i].getParameterTypes(), args ) )
return methods[i];
return null;
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// get static method
//------------------------------------------------------------------
// public static Method getStaticMethod( String classname,
// String name, Object[] args )
// public static Method getStaticMethod( Class type,
// String name, Object[] args )
// public static Method getStaticMethod( String classname, String name,
// Object[] args, boolean all )
// public static Method getStaticMethod( Class type, String name,
// Object[] args, boolean all )
//------------------------------------------------------------------
/**
* classname で示されるクラスの public static メソッドのうち、
* name という名前で args を Type.parse した後
* 受け入れることができるものを得る。
*
* @param classname クラス名。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Method getStaticMethod( String classname,
String name,
Object[] args )
throws ClassNotFoundException {
return MethodUtil.getStaticMethod( Class.forName( classname ),
name,
args,
false );
}
/**
* type の public staticなメソッドのうち、
* name という名前で args を Type.parse した後
* 受け入れることができるものを得る。
*
* @param type 型情報。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*/
public static Method getStaticMethod( Class type,
String name,
Object[] args ){
return MethodUtil.getStaticMethod( type, name, args, false );
}
/**
* classname で示されるクラスの static メソッドのうち、
* name という名前で args を Type.parse した後
* 受け入れることができるものを得る。
*
* @param classname クラス名。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
* @param all public のメソッドのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのメソッドを検索するなら true。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Method getStaticMethod( String classname,
String name,
Object[] args,
boolean all )
throws ClassNotFoundException {
return MethodUtil.getStaticMethod( Class.forName( classname ),
name,
args,
all );
}
/**
* type の static メソッドのうち、name という名前で args を
* Type.parse した後 受け入れることができるものを得る。
*
* @param type 型情報。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
* @param all public のメソッドのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのメソッドを検索するなら true。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*/
public static Method getStaticMethod( Class type,
String name,
Object[] args,
boolean all ){
Method[] methods = all
? type.getDeclaredMethods()
: type.getMethods();
for( int i = 0 ; i < methods.length ; i++ )
if( methods[i].getName().equals( name )
&& Modifier.isStatic( methods[i].getModifiers() )
&& Type.matchFullAll( methods[i].getParameterTypes(), args ) )
return methods[i];
for( int i = 0 ; i < methods.length ; i++ )
if( methods[i].getName().equals( name )
&& Modifier.isStatic( methods[i].getModifiers() )
&& Type.matchRestrictAll( methods[i].getParameterTypes(), args ) )
return methods[i];
for( int i = 0 ; i < methods.length ; i++ )
if( methods[i].getName().equals( name )
&& Modifier.isStatic( methods[i].getModifiers() )
&& Type.matchAll( methods[i].getParameterTypes(), args ) )
return methods[i];
return null;
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// get match full static method
//------------------------------------------------------------------
// public static Method getMatchFullStaticMethod( String classname,
// String name, Object[] args )
// public static Method getMatchFullStaticMethod( Class type,
// String name, Object[] args )
// public static Method getMatchFullStaticMethod( String classname,
// String name, Object[] args, boolean all )
// public static Method getMatchFullStaticMethod( Class type,
// String name, Object[] args, boolean all )
//------------------------------------------------------------------
/**
* classname で示されるクラスの public static メソッドのうち、
* name という名前で args を 直接受け入れることができるものを得る。
*
* @param classname クラス名。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Method getMatchFullStaticMethod( String classname,
String name,
Object[] args )
throws ClassNotFoundException {
return MethodUtil.getMatchFullStaticMethod( Class.forName( classname ),
name,
args,
false );
}
/**
* type の public staticなメソッドのうち、
* name という名前で args を直接受け入れることができるものを得る。
*
* @param type 型情報。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*/
public static Method getMatchFullStaticMethod( Class type,
String name,
Object[] args ){
return MethodUtil.getMatchFullStaticMethod( type, name, args, false );
}
/**
* classname で示されるクラスの static メソッドのうち、
* name という名前で args を直接受け入れることができるものを得る。
*
* @param classname クラス名。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
* @param all public のメソッドのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのメソッドを検索するなら true。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*
* @exception ClassNotFoundException
* classname で示されるクラスが存在しない場合
*/
public static Method getMatchFullStaticMethod( String classname,
String name,
Object[] args,
boolean all )
throws ClassNotFoundException {
return MethodUtil.getMatchFullStaticMethod( Class.forName( classname ),
name,
args,
all );
}
/**
* type の static メソッドのうち、name という名前で
* args を直接受け入れることができるものを得る。
*
* @param type 型情報。
* @param name 検索するメソッド名。
* @param args 引数配列。
* null を含めても良いが、null を使用した場合は
* Object のサブクラスであれば全てマッチしてしまうため、
* 目的のメソッド以外のものが見つかる可能性がある。
* @param all public のメソッドのみを検索するなら false。
* public, protected, private, パッケージプライベートの
* 全てのメソッドを検索するなら true。
*
* @return args を引数に取ることができる nameという名前の メソッド。
* 見つからなければ null。
*/
public static Method getMatchFullStaticMethod( Class type,
String name,
Object[] args,
boolean all ){
Method[] methods = all
? type.getDeclaredMethods()
: type.getMethods();
for( int i = 0 ; i < methods.length ; i++ )
if( methods[i].getName().equals( name )
&& Modifier.isStatic( methods[i].getModifiers() )
&& Type.matchFullAll( methods[i].getParameterTypes(), args ) )
return methods[i];
return null;
}
}
//end of Method.java
jp/gr/java_conf/dangan/lang/reflect/Type.java 100644 0 0 43664 7546162600 16560 0 ustar 0 0 //start of Type.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* Type.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.lang.reflect;
//import classes and interfaces
import java.math.BigInteger;
//import exceptions
import java.lang.IllegalArgumentException;
import java.lang.NumberFormatException;
/**
* Reflection の機能を扱いやすくするように
* 型情報を扱うユーティリティクラス。
*
*
* -- revision history --
* $Log: Type.java,v $
* Revision 1.0 2002/10/01 00:00:00 dangan
* first edition
* add to version control
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class Type{
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private Type()
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private Type(){ }
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// utility methods for type matching
//------------------------------------------------------------------
// public static boolean matchFullAll( Class[] types, Object[] args )
// public static boolean matchRestrictAll( Class[] types, Object[] args )
// public static boolean matchAll( Class[] types, Object[] args )
//------------------------------------------------------------------
/**
* args が変換無しで types と一致するかを得る。
*
* @param types 型情報配列
* @param args 判定対象のオブジェクト配列
*
* @return args が types に一致すれば true。
* 違えば flase。
*/
public static boolean matchFullAll( Class[] types, Object[] args ){
boolean match = ( types.length == args.length );
for( int i = 0 ; i < types.length ; i++ )
match = match && Type.matchFull( types[i], args[i] );
return match;
}
/**
* args が Type.parse による変換を伴えば
* types と一致するかを得る。
* matchAll() より厳密に判定する。
*
* @param types 型情報配列
* @param args 判定対象のオブジェクト配列
*
* @return args が types に一致すれば true。
* 違えば flase。
*/
public static boolean matchRestrictAll( Class[] types, Object[] args ){
boolean match = ( types.length == args.length );
for( int i = 0 ; i < types.length ; i++ )
match = match && Type.matchRestrict( types[i], args[i] );
return match;
}
/**
* args が Type.parse による変換を伴えば
* types と一致するかを得る。
*
* @param types 型情報配列
* @param args 判定対象のオブジェクト配列
*
* @return args が types に一致すれば true。
* 違えば flase。
*/
public static boolean matchAll( Class[] types, Object[] args ){
boolean match = ( types.length == args.length );
for( int i = 0 ; i < types.length ; i++ )
match = match && Type.match( types[i], args[i] );
return match;
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// type matching
//------------------------------------------------------------------
// public static boolean matchFull( Class type, Object obj )
// public static boolean matchRestrict( Class type, Object obj )
// public static boolean match( Class type, Object obj )
//------------------------------------------------------------------
/**
* obj が変換無しで type と一致するかを得る。
*
* @param type 型情報
* @param obj 判定対象のオブジェクト
*
* @return obj が type の実体であれば true。
* 違えば false。
*/
public static boolean matchFull( Class type, Object obj ){
if( type.isInstance( obj ) ){
return true;
}else if( !type.isPrimitive() && obj == null ){
return true;
}else if( type.equals( Boolean.TYPE ) && obj instanceof Boolean ){
return true;
}else if( type.equals( Byte.TYPE ) && obj instanceof Byte ){
return true;
}else if( type.equals( Short.TYPE ) && obj instanceof Short ){
return true;
}else if( type.equals( Character.TYPE ) && obj instanceof Character ){
return true;
}else if( type.equals( Integer.TYPE ) && obj instanceof Integer ){
return true;
}else if( type.equals( Long.TYPE ) && obj instanceof Long ){
return true;
}else if( type.equals( Float.TYPE ) && obj instanceof Float ){
return true;
}else if( type.equals( Double.TYPE ) && obj instanceof Double ){
return true;
}else{
return false;
}
}
/**
* obj が type の実体であるかを得る。
* type が数値を示すプリミティブ型
* ( byte, short, int, long, float, double のいずれか )を
* であり、かつ obj がそれらのプリミティブのラッパ型、
* ( Byte, Short, Integer, Long, Float, Double のいずれか )
* のインスタンスである場合 変換可能と判断して true を返す。
*
* @param type 型情報
* @param obj 判定対象のオブジェクト
*
* @return obj が type の実体であれば true。
* 違えば false。
*/
public static boolean matchRestrict( Class type, Object obj ){
if( Type.matchFull( type, obj ) ){
return true;
}else if( ( type.equals( Byte.TYPE ) || type.equals( Short.TYPE )
|| type.equals( Integer.TYPE ) || type.equals( Long.TYPE )
|| type.equals( Float.TYPE ) || type.equals( Double.TYPE ) )
&& ( obj instanceof Byte || obj instanceof Short
|| obj instanceof Integer || obj instanceof Long
|| obj instanceof Float || obj instanceof Double ) ){
return true;
}else{
return false;
}
}
/**
* obj が type の実体であるかを得る。
* obj が Type.parse( type, obj ) で変換可能な場合
* trueを返す。
*
* @param type 型情報
* @param obj 判定対象のオブジェクト
*
* @return obj が type の実体であれば true。
* 違えば false。
*/
public static boolean match( Class type, Object obj ){
final String str = ( obj == null ? null : obj.toString() );
if( Type.matchRestrict( type, obj ) ){
return true;
}else if( type.equals( String.class ) ){
return true;
}else if( !type.isPrimitive() && "NULL".equalsIgnoreCase( str ) ){
return true;
}else if( ( type.equals( Byte.class ) || type.equals( Byte.TYPE )
|| type.equals( Short.class ) || type.equals( Short.TYPE )
|| type.equals( Integer.class ) || type.equals( Integer.TYPE )
|| type.equals( Long.class ) || type.equals( Long.TYPE )
|| type.equals( Float.class ) || type.equals( Float.TYPE )
|| type.equals( Double.class ) || type.equals( Double.TYPE ) )
&& ( obj instanceof Number
|| ( obj != null && Type.isLongString( obj.toString() ) )
|| ( obj != null && Type.isDoubleString( obj.toString() ) ) ) ){
return true;
}else if( ( type.equals( Boolean.TYPE ) || type.equals( Boolean.class ) )
&& ( "TRUE".equalsIgnoreCase( str ) || "FALSE".equalsIgnoreCase( str ) ) ){
return true;
}else if( ( type.equals( Character.class ) || type.equals( Character.TYPE ) )
&& obj instanceof String
&& ( str.length() == 1 || Type.isUnicodeEscape( str ) ) ){
return true;
}else{
return false;
}
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// parse
//------------------------------------------------------------------
// public static Object[] parseAll( Class[] types, Object[] args )
// public static Object parse( Class type, Object obj )
//------------------------------------------------------------------
/**
* Factory.matchAll( types, args ) でマッチした args を
* 一括して types で示される型に変換する。
*
* @param types 変換する型情報配列
* @param args 変換対象のオブジェクト配列
*
* @return 変換後のオブジェクト配列
*
* @exception IllegalAccessError
* args を types に変換不可能な場合。
*/
public static Object[] parseAll( Class[] types, Object[] args ){
if( types.length == args.length ){
Object[] objs = new Object[ args.length ];
for( int i = 0 ; i < args.length ; i++ )
objs[i] = Type.parse( types[i], args[i] );
return objs;
}else{
throw new IllegalArgumentException();
}
}
/**
* Factory.match( type, obj ) でマッチした obj を
* type で示される型に変換する。
*
* @param type 変換する型情報
* @param obj 変換対象のオブジェクト
*
* @return 変換後のオブジェクト
*
* @exception IllegalArgumentException
* Factory.match( type, obj ) でマッチしていない obj を
* 変換しようとした場合。
*/
public static Object parse( Class type, Object obj ){
final String str = ( obj == null ? null : obj.toString() );
if( type.isInstance( obj ) ){
return obj;
}else if( !type.isPrimitive()
&& !type.equals( String.class )
&& ( obj == null || "NULL".equalsIgnoreCase( str ) ) ){
return null;
}else if( type.equals( String.class ) ){
return str;
}else if( ( type.equals( Byte.class ) || type.equals( Byte.TYPE )
|| type.equals( Short.class ) || type.equals( Short.TYPE )
|| type.equals( Integer.class ) || type.equals( Integer.TYPE )
|| type.equals( Long.class ) || type.equals( Long.TYPE )
|| type.equals( Float.class ) || type.equals( Float.TYPE )
|| type.equals( Double.class ) || type.equals( Double.TYPE ) )
&& ( obj instanceof Number
|| ( obj != null && Type.isLongString( str ) )
|| ( obj != null && Type.isDoubleString( str ) ) ) ){
Number num = null;
if( obj instanceof Number ){
num = (Number)obj;
}else{
try{
if( Type.isLongString( str ) )
num = new Long( Long.parseLong( str ) );
else
num = new Double( str );
}catch( NumberFormatException exception ){
num = Type.parseHexadecimal( str.substring( 2 ) );
}
}
if( type.equals( Byte.class ) || type.equals( Byte.TYPE ) ){
return new Byte( num.byteValue() );
}else if( type.equals( Short.class ) || type.equals( Short.TYPE ) ){
return new Short( num.shortValue() );
}else if( type.equals( Integer.class ) || type.equals( Integer.TYPE ) ){
return new Integer( num.intValue() );
}else if( type.equals( Long.class ) || type.equals( Long.TYPE ) ){
return new Long( num.longValue() );
}else if( type.equals( Float.class ) || type.equals( Float.TYPE ) ){
return new Float( num.floatValue() );
}else{
return new Double( num.doubleValue() );
}
}else if( type.equals( Boolean.class )
|| type.equals( Boolean.TYPE ) ){
if( "TRUE".equalsIgnoreCase( str ) ){
return new Boolean( true );
}else if( "FALSE".equalsIgnoreCase( str ) ){
return new Boolean( false );
}
}else if( ( type.equals( Character.class )
|| type.equals( Character.TYPE ) )
&& obj != null ){
if( str.length() == 1 ){
return new Character( str.charAt( 0 ) );
}else if( Type.isUnicodeEscape( str ) ){
return new Character( (char)Type.parseHexadecimal( str.substring( 2 ) ).intValue() );
}
}
throw new IllegalArgumentException();
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// chack that string match the pattern of type.
//------------------------------------------------------------------
// private static boolean isUnicodeEscape( String str )
// private static boolean isLongString( String str )
// private static boolean isDoubleString( String str )
//------------------------------------------------------------------
/**
* str が ユニコードエスケープされた1文字であるかを得る。
*
* @param str 文字列
*
* @return str がユニコードエスケープされた1文字である場合
*/
private static boolean isUnicodeEscape( String str ){
if( str.length() == 6
&& str.startsWith( "\\u" )
&& Type.isHexadecimal( str.substring( 2 ) ) ){
return true;
}else{
return false;
}
}
/**
* str が確実に Integer を示す文字列であるかを得る。
*
* @param str 文字列
*
* @return str が確実に Integer を示す文字列なら true。
* 違えば false。
*/
private static boolean isLongString( String str ){
try{
Long.parseLong( str );
return true;
}catch( NumberFormatException exception ){
}
if( str.startsWith( "0x" ) && Type.isHexadecimal( str.substring( 2 ) ) ){
BigInteger val = Type.parseHexadecimal( str.substring( 2 ) );
final BigInteger zero = new BigInteger( "0" );
final BigInteger limit = new BigInteger( "FFFFFFFFFFFFFFFF", 16 );
if( zero.compareTo( val ) <= 0 && val.compareTo( limit ) <= 0 )
return true;
else
return false;
}else{
return false;
}
}
/**
* str が確実に Double を示す文字列であるかを得る。
*
* @param str 文字列
*
* @return str が確実に Integer を示す文字列なら true。
* 違えば false。
*/
private static boolean isDoubleString( String str ){
try{
Double num = Double.valueOf( str );
if( !num.isInfinite()
|| str.equals( "Infinity" )
|| str.equals( "-Infinity" ) )
return true;
else
return false;
}catch( NumberFormatException exception ){
}
return false;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// processing hexadecimal
//------------------------------------------------------------------
// private static boolean isHexadecimal( String str )
// private static long perseHexadecimal( String str )
//------------------------------------------------------------------
/**
* 文字列が 16進の文字列かを判定する。
*
* @param str 判定対象の文字列
*
* @return str が16進の文字列であれば true。
* 違えば false。
*/
private static boolean isHexadecimal( String str ){
final String hexadecimal = "0123456789ABCDEF";
str = str.toUpperCase();
if( 0 < str.length() ){
for( int i = 0 ; i < str.length() ; i++ )
if( hexadecimal.indexOf( str.charAt( i ) ) < 0 )
return false;
return true;
}else{
return false;
}
}
/**
* 文字列を 16進の文字列として解釈し、値を得る。
*
* @param str 文字列
*
* @return str を16進数として解釈した値。
* str が16進数でない場合の結果は不定。
*/
private static BigInteger parseHexadecimal( String str ){
return new BigInteger( str, 16 );
}
}
//end of Type.java
jp/gr/java_conf/dangan/util/lha/BadHuffmanTableException.java 100644 0 0 5245 7517367000 21605 0 ustar 0 0 //start of BadHuffmanTableException.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* BadHuffmanTableException.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
//import exceptions
import java.io.IOException;
/**
* BlockHuffman.LenListToCodeList() 内で、
* 渡された LenList ( ハフマン符号長の表 )が不正なため、
* ハフマン符号を生成できない事を示す。
*
*
* -- revision history --
* $Log: BadHuffmanTableException.java,v $
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [maintenance]
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class BadHuffmanTableException extends IOException{
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public BadHuffmanTableException()
// public BadHuffmanTableException( String message )
//------------------------------------------------------------------
/**
* 新しい BadHuffmanTableException を構築する。
*/
public BadHuffmanTableException(){
super();
}
/**
* 新しい BadHuffmanTableException を構築する。
*
* @param message 詳細なメッセージ
*/
public BadHuffmanTableException( String message ){
super( message );
}
}
//end of BadHuffmanTableException.java
jp/gr/java_conf/dangan/util/lha/BinaryTreeSearch.java 100644 0 0 61205 7523610600 20165 0 ustar 0 0 //start of BinaryTreeSearch.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* BinaryTreeSearch.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import jp.gr.java_conf.dangan.util.lha.LzssOutputStream;
import jp.gr.java_conf.dangan.util.lha.LzssSearchMethod;
//import exceptions
/**
* 二分木を使用した LzssSearchMethod の実装。
*
* データ圧縮ハンドブック[改定第二版]
* M.ネルソン/J.-L.ゲィリー 著
* 萩原剛志・山口英 訳
* ISBN4-8101-8605-9
* 5728円(税抜き,当方の購入当時の価格)
*
* を参考にした。
* 二分木では、最長一致を見つけることはできるが、
* 最も近い一致を見つけられるとは限らないため、
* LZSSで 一致位置が近い場所に偏る事を
* 利用するような -lh5- のような圧縮法では、
* 圧縮率はいくらか低下する。
*
*
* -- revision history --
* $Log: BinaryTreeSearch.java,v $
* Revision 1.0 2002/08/06 00:00:00 dangan
* add to version control
* [change]
* LzssSearchMethod のインタフェイス変更にあわせてインタフェイス変更
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class BinaryTreeSearch implements LzssSearchMethod{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// private static final int UNUSED
// private static final int ROOT_NODE
//------------------------------------------------------------------
/**
* 使用されていない事を示す値。
* parent[node] に UNUSED がある場合は node は未使用のnodeである。
* small[node], large[node] に UNUSED がある場合は
* 二分木のそちら側には節が無い事を示す。
*/
private static final int UNUSED = -1;
/**
* 二分木の根を示す値。
* parent[node] に ROOT_NODE がある場合は node は二分木の根である。
*/
private static final int ROOT_NODE = -2;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private int DictionarySize
// private int MaxMatch
// private int Threshold
//------------------------------------------------------------------
/**
* LZSS辞書サイズ。
*/
private int DictionarySize;
/**
* LZSS圧縮に使用される値。
* 最大一致長を示す。
*/
private int MaxMatch;
/**
* LZSS圧縮に使用される閾値。
* 一致長が この値以上であれば、圧縮コードを出力する。
*/
private int Threshold;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// text buffer
//------------------------------------------------------------------
// private byte[] TextBuffer
// private int DictionaryLimit
//------------------------------------------------------------------
/**
* LZSS圧縮を施すためのバッファ。
* position を境に 前半は辞書領域、
* 後半は圧縮を施すためのデータの入ったバッファ。
* LzssSearchMethodの実装内では読み込みのみ許される。
*/
private byte[] TextBuffer;
/**
* 辞書の限界位置。
* TextBuffer前半の辞書領域にデータが無い場合に
* 辞書領域にある不定のデータ(Javaでは0)を使用
* して圧縮が行われるのを抑止する。
*/
private int DictionaryLimit;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// binary tree
//------------------------------------------------------------------
// private int root
// private int[] parent
// private int[] small
// private int[] large
// private int[] dummy
//------------------------------------------------------------------
/**
* 二分木の根のデータパタンの開始位置を示す。
*/
private int root;
/**
* 親のデータパタンの開始位置を示す。
*/
private int[] parent;
/**
* 小さい子のデータパタンの開始位置を示す。
*/
private int[] small;
/**
* 大きい子のデータパタンの開始位置を示す。
*/
private int[] large;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private BinaryTreeSearch()
// public BinaryTreeSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
*使用不可
*/
private BinaryTreeSearch(){ }
/**
* 二分木を使用した LzssSearchMethod を構築する。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最長一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
*/
public BinaryTreeSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer ){
this.DictionarySize = DictionarySize;
this.MaxMatch = MaxMatch;
this.Threshold = Threshold;
this.TextBuffer = TextBuffer;
this.DictionaryLimit = this.DictionarySize;
this.root = BinaryTreeSearch.UNUSED;
this.parent = new int[ this.DictionarySize ];
this.large = new int[ this.DictionarySize ];
this.small = new int[ this.DictionarySize ];
for( int i = 0 ; i < this.parent.length ; i++ ){
this.parent[i] = BinaryTreeSearch.UNUSED;
}
}
//------------------------------------------------------------------
// methods of jp.gr.java_conf.dangan.util.lha.LzssSearchMethod
//------------------------------------------------------------------
// public void put( int position )
// public int searchAndPut( int position )
// public int search( int position, int lastPutPos )
// public void slide()
// public int putRequires()
//------------------------------------------------------------------
/**
* position から始まるデータパタンを二分木に登録する。
*
* @param position TextBuffer内のデータパタンの開始位置
*/
public void put( int position ){
//------------------------------------------------------------------
// 二分木から最も古いデータパタンを削除
this.deleteNode( position - this.DictionarySize );
//------------------------------------------------------------------
// 二分木から position を挿入する位置を検索
int parentpos = this.root;
int scanpos = this.root;
byte[] buf = this.TextBuffer;
int max = position + this.MaxMatch;
int p = 0;
int s = 0;
int len = 0;
while( scanpos != BinaryTreeSearch.UNUSED ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ){
//完全一致を発見
this.replaceNode( scanpos, position );
return;
}
}
parentpos = scanpos;
scanpos = ( buf[ s ] < buf[ p ]
? this.large[ scanpos & ( this.DictionarySize - 1 ) ]
: this.small[ scanpos & ( this.DictionarySize - 1 ) ] );
}
//------------------------------------------------------------------
// position から始まるデータパタンを 二分木に登録
if( this.root != BinaryTreeSearch.UNUSED ){
this.addNode( parentpos, position, p - position );
}else{
this.root = position;
int node = position & ( this.DictionarySize - 1 );
this.parent[ node ] = BinaryTreeSearch.ROOT_NODE;
this.small[ node ] = BinaryTreeSearch.UNUSED;
this.large[ node ] = BinaryTreeSearch.UNUSED;
}
}
/**
* 二分木に登録されたデータパタンから
* position から始まるデータパタンと
* 最長の一致を持つものを検索し、
* 同時に position から始まるデータパタンを
* 二分木に登録する。
*
* @param position TextBuffer内のデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int searchAndPut( int position ){
//------------------------------------------------------------------
// 二分木から最も古いデータパタンを削除
this.deleteNode( position - this.DictionarySize );
//------------------------------------------------------------------
// 二分木から最長一致を検索
int matchlen = -1;
int matchpos = this.root;
int parentpos = this.root;
int scanpos = this.root;
byte[] buf = this.TextBuffer;
int max = position + this.MaxMatch;
int p = 0;
int s = 0;
int len = 0;
while( scanpos != BinaryTreeSearch.UNUSED ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ){
//完全一致を発見
this.replaceNode( scanpos, position );
return LzssOutputStream.createSearchReturn( this.MaxMatch, scanpos );
}
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
}else if( matchlen == len && matchpos < scanpos ){
matchpos = scanpos;
}
parentpos = scanpos;
scanpos = ( buf[ s ] < buf[ p ]
? this.large[ scanpos & ( this.DictionarySize - 1 ) ]
: this.small[ scanpos & ( this.DictionarySize - 1 ) ] );
}
//------------------------------------------------------------------
// position から始まるデータパタンを 二分木に登録
if( this.root != BinaryTreeSearch.UNUSED ){
this.addNode( parentpos, position, len );
}else{
this.root = position;
int node = position & ( this.DictionarySize - 1 );
this.parent[ node ] = BinaryTreeSearch.ROOT_NODE;
this.small[ node ] = BinaryTreeSearch.UNUSED;
this.large[ node ] = BinaryTreeSearch.UNUSED;
}
//------------------------------------------------------------------
// メソッドの先頭で削除された
// 最も遠いデータパタンと比較
scanpos = position - this.DictionarySize;
if( this.DictionaryLimit <= scanpos ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
}
}
//------------------------------------------------------------------
// 最長一致を呼び出し元に返す。
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* 二分木に登録されたデータパタンを検索し
* position から始まるデータパタンと
* 最長の一致を持つものを得る。
* TextBuffer.length < position + MaxMatch
* となるような position では、
* 二分木を完全に走査できないため
* 最長一致を得られるとは限らない。
*
* @param position TextBuffer内のデータパタンの開始位置。
* @param lastPutPos 最後に登録したデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int search( int position, int lastPutPos ){
//------------------------------------------------------------------
// 二分木に登録されていないデータパタンを
// 単純な逐次検索で検索する。
int matchlen = this.Threshold - 1;
int matchpos = position;
int scanpos = position - 1;
int scanlimit = Math.max( this.DictionaryLimit, lastPutPos );
byte[] buf = this.TextBuffer;
int max = Math.min( this.TextBuffer.length,
position + this.MaxMatch );
int s = 0;
int p = 0;
int len = 0;
while( scanlimit < scanpos ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
if( max <= p ) break;
}
scanpos--;
}
//------------------------------------------------------------------
// 二分木を探索
scanpos = this.root;
scanlimit = Math.max( this.DictionaryLimit,
position - this.DictionarySize );
while( scanpos != BinaryTreeSearch.UNUSED ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
if( p < max ){
len = p - position;
if( scanlimit <= scanpos ){
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
}else if( matchlen == len && matchpos < scanpos ){
matchpos = scanpos;
}
}
scanpos = ( buf[ s ] < buf[ p ]
? this.large[ scanpos & ( this.DictionarySize - 1 ) ]
: this.small[ scanpos & ( this.DictionarySize - 1 ) ] );
}else{
break;
}
}
//------------------------------------------------------------------
// 最長一致を呼び出し元に返す。
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* TextBuffer内のpositionまでのデータを前方へ移動する際、
* それに応じて 二分木を構成するデータも
* TextBuffer内のデータと矛盾しないように前方へ移動する処理を行う。
*
* @param slideWidth ずらす幅
* @param slideEnd ずらすデータの終端 + 1(データ転送先)
*/
public void slide(){
this.DictionaryLimit = Math.max( 0, this.DictionaryLimit - this.DictionarySize );
this.root -= this.DictionarySize;
this.slideTree( this.parent );
this.slideTree( this.small );
this.slideTree( this.large );
}
/**
* put() または searchAndPut() を使用して
* データパタンを二分木に登録する際に
* 必要とするデータ量を得る。
* 二分木は登録の際にデータパタンを構成する
* 全て(MaxMatchバイト)のデータを必要とする。
*
* @return コンストラクタで与えた MaxMatch
*/
public int putRequires(){
return this.MaxMatch;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// manipulate node
//------------------------------------------------------------------
// private void addNode( int addpos, int position, int len )
// private void deleteNode( int position )
// private void contractNode( int oldpos, int newpos )
// private void replaceNode( int oldpos, int newpos )
//------------------------------------------------------------------
/**
* parentpos のデータパタンの子として
* position から始まるデータパタンを二分木に登録する。
* parentpos と position のデータパタンは len バイト一致する。
* position の位置のノードはあらかじめ deleteNode 等で
* UNUSED の状態にしておくこと。
*
* @param parentpos 親のデータパタンのTextBuffer内の開始位置
* @param position 新規追加するデータパタンのTextBuffer内の開始位置
* @param len 親のデータパタンと新規追加するデータパタンの一致長
*/
private void addNode( int parentpos, int position, int len ){
int parentnode = parentpos & ( this.DictionarySize - 1 );
int node = position & ( this.DictionarySize - 1 );
if( this.TextBuffer[ parentpos + len ] < this.TextBuffer[ position + len ] ){
this.large[ parentnode ] = position;
}else{
this.small[ parentnode ] = position;
}
this.parent[ node ] = parentpos;
this.small[ node ] = BinaryTreeSearch.UNUSED;
this.large[ node ] = BinaryTreeSearch.UNUSED;
}
/**
* position から始まるデータパタンを二分木から削除する。
*
* @param position 削除するデータパタンの開始位置
*/
private void deleteNode( int position ){
int node = position & ( this.DictionarySize - 1 );
if( this.parent[ node ] != BinaryTreeSearch.UNUSED ){
if( this.small[ node ] == BinaryTreeSearch.UNUSED
&& this.large[ node ] == BinaryTreeSearch.UNUSED ){
this.contractNode( position, BinaryTreeSearch.UNUSED );
}else if( this.small[ node ] == BinaryTreeSearch.UNUSED ){
this.contractNode( position, this.large[ node ] );
}else if( this.large[ node ] == BinaryTreeSearch.UNUSED ){
this.contractNode( position, this.small[ node ] );
}else{
int replace = this.findNext( position );
this.deleteNode( replace );
this.replaceNode( position, replace );
}
}
}
/**
* 子に newpos しか持たない oldpos を, newpos で置き換える。
* oldpos は二分木から削除される。
*
* @param oldpos 削除するデータパタンの開始位置
* @param newpos oldposに置き換わるデータパタンの開始位置
*/
private void contractNode( int oldpos, int newpos ){
int oldnode = oldpos & ( this.DictionarySize - 1 );
int newnode = newpos & ( this.DictionarySize - 1 );
int parentpos = this.parent[ oldnode ];
int parentnode = parentpos & ( this.DictionarySize - 1 );
if( parentpos != BinaryTreeSearch.ROOT_NODE ){
if( oldpos == this.small[ parentnode ] ){
this.small[ parentnode ] = newpos;
}else{
this.large[ parentnode ] = newpos;
}
}else{
this.root = newpos;
}
if( newpos != BinaryTreeSearch.UNUSED ){
this.parent[ newnode ] = parentpos;
}
this.parent[ oldnode ] = BinaryTreeSearch.UNUSED;
}
/**
* oldpos を二分木に含まれない新しいデータパタン newpos で置き換える。
* newpos が二分木に含まれているような場合には、
* いったんdeleteNode(newpos) するなどして、
* 二分木から外す必要がある。
* oldpos は二分木から削除される。
*
* @param oldpos 削除するデータパタンの開始位置
* @param newpos oldposに置き換わるデータパタンの開始位置
*/
private void replaceNode( int oldpos, int newpos ){
int oldnode = oldpos & ( this.DictionarySize - 1 );
int newnode = newpos & ( this.DictionarySize - 1 );
int parentpos = this.parent[ oldnode ];
int parentnode = parentpos & ( this.DictionarySize - 1 );
if( parentpos != BinaryTreeSearch.ROOT_NODE ){
if( oldpos == this.small[ parentnode ] ){
this.small[ parentnode ] = newpos;
}else{
this.large[ parentnode ] = newpos;
}
}else{
this.root = newpos;
}
this.parent[ newnode ] = parentpos;
this.small[ newnode ] = this.small[ oldnode ];
this.large[ newnode ] = this.large[ oldnode ];
if( this.small[ newnode ] != BinaryTreeSearch.UNUSED ){
this.parent[ this.small[ newnode ] & ( this.DictionarySize - 1 ) ] = newpos;
}
if( this.large[ newnode ] != BinaryTreeSearch.UNUSED ){
this.parent[ this.large[ newnode ] & ( this.DictionarySize - 1 ) ] = newpos;
}
this.parent[ oldnode ] = BinaryTreeSearch.UNUSED;
this.large[ oldnode ] = BinaryTreeSearch.UNUSED;
this.small[ oldnode ] = BinaryTreeSearch.UNUSED;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private int findNext( int position )
// private void slideTree( int[] array )
//------------------------------------------------------------------
/**
* deleteNode( position ) したときに、
* small と large の両方の葉が見つかった場合、
* position から始まるデータパタンと
* 置き換えるべきデータパタンの開始位置を探し出す。
*
* @param position 置き換えられるデータパタンの開始位置
*
* @return position から始まるデータパタンと
* 置き換えるべきデータパタンの開始位置
*/
private int findNext( int position ){
int node = position & ( this.DictionarySize - 1 );
position = this.small[ node ];
node = position & ( this.DictionarySize - 1 );
while( BinaryTreeSearch.UNUSED != this.large[ node ] ){
position = this.large[ node ];
node = position & ( this.DictionarySize - 1 );
}
return position;
}
/**
* slide() 時に、二分木の各要素を移動する。
*
* @param array 二分木を構成する配列
*/
private void slideTree( int[] array ){
for( int i = 0 ; i < array.length ; i++ ){
array[ i ] = ( 0 <= array[i]
? array[i] - this.DictionarySize
: array[i] );
}
}
}
//end of BinaryTreeSearch.java
jp/gr/java_conf/dangan/util/lha/CompressMethod.java 100644 0 0 42053 7574505600 17740 0 ustar 0 0 //start of CompressMethod.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* CompressMethod.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Properties;
//import exceptions
import java.lang.NullPointerException;
import java.lang.IllegalArgumentException;
/**
* LHAの各種定数を定義する。
*
*
* -- revision history --
* $Log: CompressMethod.java,v $
* Revision 1.1 2002/12/08 00:00:00 dangan
* [change]
* クラス名を LhaConstants から CompressMethod へと変更。
*
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [change]
* LhaUtil の connectExtractInputStream を connectDecoder として
* connectCompressOutputStream を connectEncoder として引き継ぐ。
* LhaUtil の CompressMethodTo????????? を引き継ぐ。
* [maintanance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class CompressMethod{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// compress method identifier
//------------------------------------------------------------------
// public static final String LH0
// public static final String LH1
// public static final String LH2
// public static final String LH3
// public static final String LH4
// public static final String LH5
// public static final String LH6
// public static final String LH7
// public static final String LHD
// public static final String LZS
// public static final String LZ4
// public static final String LZ5
//------------------------------------------------------------------
/**
* 圧縮形式を示す文字列。
* LH0 は 無圧縮を示す "-lh0-" である。
*/
public static final String LH0 = "-lh0-";
/**
* 圧縮形式を示す文字列。
* LH1 は前段に 4キロバイトの辞書、最大一致長60バイトの
* LZSS法、後段に 適応的ハフマン法を使用することを意味する
* "-lh1-" である。
*/
public static final String LH1 = "-lh1-";
/**
* 圧縮形式を示す文字列。
* LH2 は前段に 8キロバイトの辞書、最大一致長256バイトの
* LZSS法、後段に 適応的ハフマン法を使用することを意味する
* "-lh2-" である。
* この圧縮法は LH1 から LH5 への改良途中で試験的に
* 使われたが、現在は使用されていない。
*/
public static final String LH2 = "-lh2-";
/**
* 圧縮形式を示す文字列。
* LH3 は前段に 8キロバイトの辞書、最大一致長256バイトの
* LZSS法、後段に 静的ハフマン法を使用することを意味する
* "-lh3-" である。
* この圧縮法は LH1 から LH5 への改良途中で試験的に
* 使われたが、現在は使用されていない。
*/
public static final String LH3 = "-lh3-";
/**
* 圧縮形式を示す文字列。
* LH4 は前段に 4キロバイトの辞書、最大一致長256バイトの
* LZSS法、後段に 静的ハフマン法を使用することを意味する
* "-lh4-" である。
* この圧縮法は 1990年代前半の非力なマシン上で圧縮を行う際、
* LH5圧縮を行うだけのシステム資源を得られなかった時に使わ
* れたが、現在は殆ど使用されていない。
*/
public static final String LH4 = "-lh4-";
/**
* 圧縮形式を示す文字列。
* LH5 は前段に 8キロバイトの辞書、最大一致長256バイトの
* LZSS法、後段に 静的ハフマン法を使用することを意味する
* "-lh5-" である。
* 現在、LHAで標準で使用される圧縮法である。
*/
public static final String LH5 = "-lh5-";
/**
* 圧縮形式を示す文字列。
* LH6 は前段に 32キロバイトの辞書、最大一致長256バイトの
* LZSS法、後段に 静的ハフマン法を使用することを意味する
* "-lh6-" である。
* "-lh6-" という文字列は LH7 の圧縮法の実験に使用されて
* いた。そのため、LHAの実験版が作成した書庫には "-lh6-"
* の文字列を使用しながら LH7 形式で圧縮されているものが
* 存在するらしい。
* また この圧縮法は開発されてから 10年近く経つが未だに
* 公の場所に この圧縮法で圧縮された書庫は登録しないこと
* が望ましいとされている。
*/
public static final String LH6 = "-lh6-";
/**
* 圧縮形式を示す文字列。
* LH7 は前段に 64キロバイトの辞書、最大一致長256バイトの
* LZSS法、後段に 静的ハフマン法を使用することを意味する
* "-lh7-" である。
* また この圧縮法は開発されてから 10年近く経つが未だに
* 公の場所に この圧縮法で圧縮された書庫は登録しないこと
* が望ましいとされている。
*/
public static final String LH7 = "-lh7-";
/**
* 圧縮形式を示す文字列。
* LHD は無圧縮で、ディレクトリを格納していることを示す
* "-lhd-" である。
*/
public static final String LHD = "-lhd-";
/**
* 圧縮形式を示す文字列。
* LZS は 2キロバイトの辞書、最大一致長17バイトの
* LZSS法を使用することを示す "-lzs-" である。
* "-lzs-" は LHAが作成される前にメジャーであった
* Larc の形式であり、当時の互換性に配慮して定義さ
* れた。現在は殆ど使用されていない。
*/
public static final String LZS = "-lzs-";
/**
* 圧縮形式を示す文字列。
* LZ4 は 無圧縮を示す "-lz4-" である。
* "-lz4-" は LHAが作成される前にメジャーであった
* Larc の形式であり、当時の互換性に配慮して定義さ
* れた。現在は殆ど使用されていない。
*/
public static final String LZ4 = "-lz4-";
/**
* 圧縮形式を示す文字列。
* LZ5 は 4キロバイトの辞書、最大一致長17バイトの
* LZSS法を使用することを示す "-lz5-" である。
* "-lz5-" は LHAが作成される前にメジャーであった
* Larc の形式であり、当時の互換性に配慮して定義さ
* れた。現在は殆ど使用されていない。
*/
public static final String LZ5 = "-lz5-";
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private CompressMethod()
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ使用不可
*/
private CompressMethod(){ }
//------------------------------------------------------------------
// convert to LZSS parameter
//------------------------------------------------------------------
// public static int toDictionarySize( String method )
// public static int toThreshold( String method )
// public static int toMaxMatch( String method )
//------------------------------------------------------------------
/**
* 圧縮法識別子から 辞書サイズを得る。
*
* @param method 圧縮法識別子
*
* @return 辞書サイズ
*/
public static int toDictionarySize( String method ){
if( CompressMethod.LZS.equalsIgnoreCase( method ) ){
return 2048;
}else if( CompressMethod.LZ5.equalsIgnoreCase( method ) ){
return 4096;
}else if( CompressMethod.LH1.equalsIgnoreCase( method ) ){
return 4096;
}else if( CompressMethod.LH2.equalsIgnoreCase( method ) ){
return 8192;
}else if( CompressMethod.LH3.equalsIgnoreCase( method ) ){
return 8192;
}else if( CompressMethod.LH4.equalsIgnoreCase( method ) ){
return 4096;
}else if( CompressMethod.LH5.equalsIgnoreCase( method ) ){
return 8192;
}else if( CompressMethod.LH6.equalsIgnoreCase( method ) ){
return 32768;
}else if( CompressMethod.LH7.equalsIgnoreCase( method ) ){
return 65536;
}else if( CompressMethod.LZ4.equalsIgnoreCase( method ) ){
throw new IllegalArgumentException( method + " means no compress." );
}else if( CompressMethod.LH0.equalsIgnoreCase( method ) ){
throw new IllegalArgumentException( method + " means no compress." );
}else if( CompressMethod.LHD.equalsIgnoreCase( method ) ){
throw new IllegalArgumentException( method + " means no compress." );
}else if( method == null ){
throw new NullPointerException( "method" );
}else{
throw new IllegalArgumentException( "Unknown compress method. " + method );
}
}
/**
* 圧縮法識別子から 圧縮/非圧縮の閾値を得る。
*
* @param method 圧縮法識別子
*
* @return 圧縮/非圧縮
*/
public static int toThreshold( String method ){
if( CompressMethod.LZS.equalsIgnoreCase( method ) ){
return 2;
}else if( CompressMethod.LZ5.equalsIgnoreCase( method ) ){
return 3;
}else if( CompressMethod.LH1.equalsIgnoreCase( method ) ){
return 3;
}else if( CompressMethod.LH2.equalsIgnoreCase( method ) ){
return 3;
}else if( CompressMethod.LH3.equalsIgnoreCase( method ) ){
return 3;
}else if( CompressMethod.LH4.equalsIgnoreCase( method ) ){
return 3;
}else if( CompressMethod.LH5.equalsIgnoreCase( method ) ){
return 3;
}else if( CompressMethod.LH6.equalsIgnoreCase( method ) ){
return 3;
}else if( CompressMethod.LH7.equalsIgnoreCase( method ) ){
return 3;
}else if( CompressMethod.LZ4.equalsIgnoreCase( method ) ){
throw new IllegalArgumentException( method + " means no compress." );
}else if( CompressMethod.LH0.equalsIgnoreCase( method ) ){
throw new IllegalArgumentException( method + " means no compress." );
}else if( CompressMethod.LHD.equalsIgnoreCase( method ) ){
throw new IllegalArgumentException( method + " means no compress." );
}else if( method == null ){
throw new NullPointerException( "method" );
}else{
throw new IllegalArgumentException( "Unknown compress method. " + method );
}
}
/**
* 圧縮法識別子から 最大一致長を得る。
*
* @param method 圧縮法識別子
*
* @return 最大一致長
*/
public static int toMaxMatch( String method ){
if( CompressMethod.LZS.equalsIgnoreCase( method ) ){
return 17;
}else if( CompressMethod.LZ5.equalsIgnoreCase( method ) ){
return 18;
}else if( CompressMethod.LH1.equalsIgnoreCase( method ) ){
return 60;
}else if( CompressMethod.LH2.equalsIgnoreCase( method ) ){
return 256;
}else if( CompressMethod.LH3.equalsIgnoreCase( method ) ){
return 256;
}else if( CompressMethod.LH4.equalsIgnoreCase( method ) ){
return 256;
}else if( CompressMethod.LH5.equalsIgnoreCase( method ) ){
return 256;
}else if( CompressMethod.LH6.equalsIgnoreCase( method ) ){
return 256;
}else if( CompressMethod.LH7.equalsIgnoreCase( method ) ){
return 256;
}else if( CompressMethod.LZ4.equalsIgnoreCase( method ) ){
throw new IllegalArgumentException( method + " means no compress." );
}else if( CompressMethod.LH0.equalsIgnoreCase( method ) ){
throw new IllegalArgumentException( method + " means no compress." );
}else if( CompressMethod.LHD.equalsIgnoreCase( method ) ){
throw new IllegalArgumentException( method + " means no compress." );
}else if( method == null ){
throw new NullPointerException( "method" );
}else{
throw new IllegalArgumentException( "Unknown compress method. " + method );
}
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// connect encoder/decoder
//------------------------------------------------------------------
// public static OutputStream connectEncoder( OutputStream out,
// String method, Properties property )
// public static InputStream connectDecoder( InputStream in,
// String method, Properties property, long length )
//------------------------------------------------------------------
/**
* property に設定された生成式を利用して
* method の圧縮法でデータを圧縮し、outに出力するストリームを構築する。
*
* @param out 圧縮データ出力先のストリーム
* @param method 圧縮法識別子
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*
* @return method の圧縮法でデータを圧縮し、outに出力するストリーム
*/
public static OutputStream connectEncoder( OutputStream out,
String method,
Properties property ){
String key = "lha." + CompressMethod.getCore( method ) + ".encoder";
String generator = property.getProperty( key );
if( generator == null ){
generator = LhaProperty.getProperty( key );
}
String packages = property.getProperty( "lha.packages" );
if( packages == null ){
packages = LhaProperty.getProperty( "lha.packages" );
}
Hashtable substitute = new Hashtable();
substitute.put( "out", out );
return (OutputStream)LhaProperty.parse( generator,
substitute,
packages );
}
/**
* property に設定された生成式を利用して
* in から method の圧縮法で圧縮されたデータを解凍し
* 供給する入力ストリームを構築する。
*
* @param in 圧縮データを供給するストリーム
* @param method 圧縮法識別子
* @param property 各圧縮形式に対応した復号器の生成式等が含まれるプロパティ
*
* @return in から method の圧縮法で圧縮されたデータを解凍し
* 供給する入力ストリームを構築する。
*/
public static InputStream connectDecoder( InputStream in,
String method,
Properties property,
long length ){
String key = "lha." + CompressMethod.getCore( method ) + ".decoder";
String generator = property.getProperty( key );
if( generator == null ){
generator = LhaProperty.getProperty( key );
}
String packages = property.getProperty( "lha.packages" );
if( packages == null ){
packages = LhaProperty.getProperty( "lha.packages" );
}
Hashtable substitute = new Hashtable();
substitute.put( "in", in );
substitute.put( "length", new Long( length ) );
return (InputStream)LhaProperty.parse( generator,
substitute,
packages );
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private static String getCore( String method )
//------------------------------------------------------------------
/**
* 圧縮法識別子 の前後の '-' を取り去って
* LhaProperty のキー lha.???.encoder / lha.???.decoder
* の ??? に入る文字列を生成する。
*
* @param method 圧縮法識別子
*
* @return キーの中心に使える文字列
*/
private static String getCore( String method ){
if( method.startsWith( "-" ) && method.endsWith( "-" ) ){
return method.substring( 1, method.lastIndexOf( '-' ) ).toLowerCase();
}else{
throw new IllegalArgumentException( "" );
}
}
}
//end of CompressMethod.java
jp/gr/java_conf/dangan/util/lha/CRC16.java 100644 0 0 24450 7517367000 15560 0 ustar 0 0 //start of CRC16.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* CRC16.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.util.zip.Checksum;
//import exceptions
/**
* CRC16値を算出するためのクラス。
*
* クラス内の定数、処理、説明は
*
* C言語によるアルゴリズム辞典
* 奥村晴彦著 技術評論社
* ISBN4-87408-414-1 C3055 2400円(購入当時)
*
* によった。
*
*
* -- revision history --
* $Log: CRC16.java,v $
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [maintanance]
* ソース整備
* タブ廃止
* ライセンス文の変更
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class CRC16 implements Checksum{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// public static final int CRC_ANSY_POLY
// public static final int CRC_ANSY_INIT
// public static final int CCITT_POLY
// public static final int CCITT_INIT
// public static final int DefaultPOLY
// public static final int DefaultINIT
//------------------------------------------------------------------
/**
* CRC-ANSY または CRC-16 として有名な
* 多項式 x^16 + x^15 + x^2 + 1 をビット表現にしたもの。
*/
public static final int CRC_ANSY_POLY = 0xA001;
/**
* LHAで使用される crc の初期値。
* 作者が勝手に設定した値であり、
* CRC-ANSY でこの値が初期値として
* 定められているかは知らない。
*/
public static final int CRC_ANSY_INIT = 0x0000;
/**
* CCITT の X.25という規格の
* 多項式 x^16 + x^12 + x^5 + 1 をビット表現にしたもの。
*/
public static final int CCITT_POLY = 0x8408;
/**
* CCITT の X.25という規格の crc の初期値。
*/
public static final int CCITT_INIT = 0xFFFF;
/**
* LHAで通常使用される、という意味でデフォルトのCRC多項式。
* CRC16.CRC_ANSY_POLY と同等である。
*/
public static final int DefaultPOLY = CRC16.CRC_ANSY_POLY;
/**
* LHAで通常使用される、という意味でデフォルトのcrcの初期値。
* CRC16.CRC_ANSY_INIT と同等である。
*/
public static final int DefaultINIT = CRC16.CRC_ANSY_INIT;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private int crc
// private int init
// private int[] crcTable
//------------------------------------------------------------------
/**
* CRC16値
*/
private int crc;
/**
* crc の初期値
*/
private int init;
/**
* CRC16値の更新用テーブル
*/
private int[] crcTable;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public CRC16()
// public CRC16( int poly )
// public CRC16( int poly, int init )
// public CRC16( int[] crcTable, int init )
//------------------------------------------------------------------
/**
* LHAで使用される 多項式と初期値を持つ CRC16を生成する。
*/
public CRC16(){
this( DefaultPOLY, DefaultINIT );
}
/**
* poly で指定される 多項式を持つ CRC16を生成する。
* 初期値は poly が CRC16.CCITT_POLY であれば
* CRC16.CCITT_INIT を そうでなければ
* CRC16.DefaultINIT を使用する。
*
* @param poly CRC16算出に使用する多項式のビット表現
*/
public CRC16( int poly ){
this( poly,
( poly == CRC16.CCITT_POLY ?
CRC16.CCITT_INIT :
CRC16.DefaultINIT ) );
}
/**
* poly で指定される 多項式と initで指定される初期値を持つ
* CRC16を生成する。
*
* @param poly CRC16算出に使用する多項式のビット表現
* @param init crc の初期値
*/
public CRC16( int poly, int init ){
this( CRC16.makeCrcTable( poly ), init );
}
/**
* crcTable で指定される CRC算出用表と
* initで指定される初期値を持つ CRC16を作成する。
*
* @param crcTable CRC16算出に使用する表
* @param init crc の初期値
*/
public CRC16( int[] crcTable, int init ){
final int BYTE_PATTERNS= 256;
if( crcTable.length == BYTE_PATTERNS ){
this.crcTable = crcTable;
this.init = init;
this.reset();
}else{
throw new IllegalArgumentException( "crcTable.length must equals 256" );
}
}
//------------------------------------------------------------------
// method of java.util.zip.Checksum
//------------------------------------------------------------------
// update
//------------------------------------------------------------------
// public void update( int byte8 )
// public void update( byte[] buffer )
// public void update( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* byte8 で指定される 1バイトのデータで crcの値を更新する。
*
* @param byte8 crcを更新する 1バイトのデータ
*/
public void update( int byte8 ){
final int BYTE_BITS = 8;
this.crc = ( this.crc >> BYTE_BITS )
^ this.crcTable[ ( this.crc ^ byte8 ) & 0xFF ];
}
/**
* buffer で指定したバイト配列で crc の値を更新する。
*
* @param buffer crcを更新する データを持つバイト配列
*/
public void update( byte[] buffer ){
this.update( buffer, 0, buffer.length );
}
/**
* buffer で指定したバイト配列で crc の値を更新する。
*
* @param buffer crcを更新する データを持つバイト配列
* @param index データの開始位置
* @param length チェックサムの更新に使うバイト数
*/
public void update( byte[] buffer, int index, int length ){
final int BYTE_BITS = 8;
while( 0 < ( index & 0x03 ) && 0 < length-- ){
this.crc = ( this.crc >> BYTE_BITS )
^ this.crcTable[ ( this.crc ^ buffer[index++] ) & 0xFF ];
}
while( 4 <= length ){
int data = ( buffer[index++] & 0xFF )
| ( ( buffer[index++] & 0xFF ) << 8 )
| ( ( buffer[index++] & 0xFF ) << 16 )
| ( buffer[index++] << 24 );
this.crc = ( this.crc >> BYTE_BITS )
^ this.crcTable[ ( this.crc ^ data ) & 0xFF ];
data >>>= BYTE_BITS;
this.crc = ( this.crc >> BYTE_BITS )
^ this.crcTable[ ( this.crc ^ data ) & 0xFF ];
data >>>= BYTE_BITS;
this.crc = ( this.crc >> BYTE_BITS )
^ this.crcTable[ ( this.crc ^ data ) & 0xFF ];
data >>>= BYTE_BITS;
this.crc = ( this.crc >> BYTE_BITS )
^ this.crcTable[ ( this.crc ^ data ) & 0xFF ];
length -= 4;
}
while( 0 < length-- ){
this.crc = ( this.crc >> BYTE_BITS )
^ this.crcTable[ ( this.crc ^ buffer[index++] ) & 0xFF ];
}
}
//------------------------------------------------------------------
// method of java.util.zip.Checksum
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void reset()
// public long getValue()
//------------------------------------------------------------------
/**
* crc 値を初期値に設定しなおす。
*/
public void reset(){
this.crc = this.init;
}
/**
* crc 値を得る。
* crc 値は 2バイトの値であり、
* 0x0000〜0xFFFFにマップされる。
*
* @return crc 値
*/
public long getValue(){
return this.crc & 0xFFFF;
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// public static int[] makeCrcTable( int init )
//------------------------------------------------------------------
/**
* CRC値算出用の 表を作成する。
*
* @param poly CRC算出用の多項式
*/
public static int[] makeCrcTable( int poly ){
final int BYTE_PATTERNS = 256;
final int BYTE_BITS = 8;
int[] crcTable = new int[BYTE_PATTERNS];
for( int i = 0 ; i < BYTE_PATTERNS ; i++ ){
crcTable[i] = i;
for( int j = 0 ; j < BYTE_BITS ; j++ ){
if( ( crcTable[i] & 1 ) != 0 ){
crcTable[i] = ( crcTable[i] >> 1 ) ^ poly;
}else{
crcTable[i] >>= 1;
}
}
}
return crcTable;
}
}
//end of CRC16.java
jp/gr/java_conf/dangan/util/lha/DynamicHuffman.java 100644 0 0 32014 7517367000 17666 0 ustar 0 0 //start of DynamicHuffman.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* DynamicHuffman.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.lang.Cloneable;
//import exceptions
import java.io.IOException;
/**
* 動的ハフマンを扱うクラス。
*
*
* -- revision history --
* $Log: DynamicHuffman.java,v $
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [bug fix]
* addLeaf() で葉の数が 1 から 2へと増加するときに
* 最初からあった葉の重さが 1 だと決め付けていた。
* [change]
* コンストラクタ DynamicHuffman( int, int ) で
* 開始時のハフマン木のサイズでなく 開始時の葉の数を渡すように変更。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の変更
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class DynamicHuffman implements Cloneable{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// public static final int ROOT
// private static final int MAX_WEIGHT
//------------------------------------------------------------------
/**
* ハフマン木のルートを示す。
*/
public static final int ROOT = 0;
/**
* ハフマン木を再構築する重さ
*/
private static final int MAX_WEIGHT = 0x8000;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// huffman tree
//------------------------------------------------------------------
// private int[] weight
// private int[] child
// private int[] parent
// private int[] leafs
// private int size
//------------------------------------------------------------------
/**
* 添え字のノードの重さを示す。
*/
private int[] weight;
/**
* 添え字のノードの子ノードのノード番号を保持する
* 兄弟特性を利用するため、
* child が 小さいノードのノード番号
* child - 1 が 大きいノードのノード番号となる。
* 葉の場合はデータをbit反転したものが入っている。
*/
private int[] child;
/**
* 添え字のノードの親ノードのノード番号を保持する
*/
private int[] parent;
/**
* 葉のノード番号を保持する。
*/
private int[] leafs;
/**
* 現在のハフマン木の大きさ
*/
private int size;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private DynamicHuffman()
// public DynamicHuffman( int count )
// public DynamicHuffman( int max, int first )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private DynamicHuffman(){ }
/**
* コンストラクタ
*
* @param count 葉の数
*/
public DynamicHuffman( int count ){
this( count, count );
}
/**
* コンストラクタ
*
* @param max 葉の最大数
* @param start 開始時の葉の数
*/
public DynamicHuffman( int max, int first ){
if( 1 <= first && first <= max ){
this.weight = new int[ max * 2 - 1 ];
this.child = new int[ max * 2 - 1 ];
this.parent = new int[ max * 2 - 1 ];
this.leafs = new int[ max ];
this.size = Math.max( 0, first * 2 - 1 );
//葉を生成していく。
int node = this.size - 1;
for( int code = 0 ; code < first ; code++, node-- ){
this.weight[ node ] = 1;
this.child[ node ] = ~code;
this.leafs[ code ] = node;
}
//枝を生成していく。
int child = this.size - 1;
while( 0 <= node && node != child ){
this.weight[node] = this.weight[child] + this.weight[child-1];
this.child[node] = child;
this.parent[child] = this.parent[child-1] = node;
child -= 2;
node--;
}
}else if( max < first ){
throw new IllegalArgumentException( "\"max\" must be larger than \"first\"." );
}else{
throw new IllegalArgumentException( "\"first\" must be one or more." );
}
}
//------------------------------------------------------------------
// method of java.lang.Object
//------------------------------------------------------------------
// public Object clone()
//------------------------------------------------------------------
/**
* このオブジェクトの現在の状態を持つコピーを作成して返す。
*
* @return このオブジェクトの現在の状態を持つコピー
*/
public Object clone(){
DynamicHuffman clone = new DynamicHuffman();
clone.weight = (int[])this.weight.clone();
clone.child = (int[])this.child.clone();
clone.parent = (int[])this.parent.clone();
clone.leafs = (int[])this.leafs.clone();
clone.size = this.size;
return clone;
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// access to huffman tree
//------------------------------------------------------------------
// public int codeToNode( int code )
// public int childNode( int node )
// public int parentNode( int node )
//------------------------------------------------------------------
/**
* データからノード番号を得る。
*
* @param code データ
*
* @return codeのノード番号
*/
public int codeToNode( int code ){
return this.leafs[code];
}
/**
* ノードが葉でないノードなら子ノードのノード番号、
* ノードが葉ならノードの持つデータを全ビット反転したものを得る。
* 子ノードのノード番号は兄弟特性と利用するため、
* node の 0 の子ノードの場合 childNode( node )
* node の 1 の子ノードの場合 childNode( node ) - 1
* となる。
*
* @param node ノード
*
* @return node の子ノードのノード番号
*/
public int childNode( int node ){
return this.child[node];
}
/**
* node の親ノードのノード番号を得る。
*
* @param node ノード
*
* @return node の親ノードのノード番号。
*/
public int parentNode( int node ){
return this.parent[node];
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// update huffman tree
//------------------------------------------------------------------
// public void update( int code )
// public void addLeaf( int code )
//------------------------------------------------------------------
/**
* code の重みが増すようにハフマン木を更新する。
*
* @param code 重みを増やす葉
*/
public void update( int code ){
if( this.weight[ DynamicHuffman.ROOT ] == DynamicHuffman.MAX_WEIGHT ){
this.rebuildTree();
}
int node = this.leafs[code];
while( DynamicHuffman.ROOT != node ){
int swapNode = node;
while( this.weight[swapNode - 1] == this.weight[node]
&& DynamicHuffman.ROOT < swapNode - 1 ){
swapNode--;
}
if( node != swapNode ) this.swap( node, swapNode );
this.weight[swapNode]++;
node = this.parent[swapNode];
}
this.weight[ DynamicHuffman.ROOT ]++;
}
/**
* ハフマン木に code を示す葉を追加する。
*
* @param code 葉の示す符号
*
* @exception IllegalStateException
* ハフマン木が十分に大きいため
* 葉が追加できない場合
*/
public void addLeaf( int code ){
if( this.size < this.weight.length - 1 ){
int last = this.size - 1;
int large = this.size;
int small = this.size + 1;
this.child[ large ] = this.child[ last ];
this.child[ small ] = ~code;
this.child[ last ] = small;
this.weight[ large ] = this.weight[ last ];
this.weight[ small ] = 0;
this.leafs[ ~this.child[ large ] ] = large;
this.leafs[ ~this.child[ small ] ] = small;
this.parent[ large ] = this.parent[ small ] = last;
this.size = small + 1;
if( last == DynamicHuffman.ROOT ){
this.weight[ last ] -= 1;
}
this.update( code );
}else{
throw new IllegalStateException();
}
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private void rebuildTree()
// private void swap( int i, int j )
//------------------------------------------------------------------
/**
* ハフマン木を再構築する。
* 重みが privateな定数 MAX_WEIGHT を超えた時に
* update(int)から呼び出される。
* 全てのノードの重みを およそ半分にする。
*/
private void rebuildTree(){
int leafCount = 0;
for( int i = 0 ; i < this.size ; i++ )
if( this.child[i] < 0 ){
this.weight[leafCount] = ( this.weight[i] + 1 ) / 2;
this.child[leafCount] = this.child[i];
leafCount++;
}
leafCount--;
int position = this.size - 1;
int leafPosition = this.size - 2;
while( 0 <= position ){
while( leafPosition <= position ){
this.leafs[~this.child[leafCount]] = position;
this.weight[ position ] = this.weight[ leafCount ];
this.child[ position-- ] = this.child[ leafCount-- ];
}
int weight = this.weight[leafPosition]
+ this.weight[leafPosition + 1];
while( 0 <= leafCount && this.weight[leafCount] <= weight ){
this.leafs[~this.child[leafCount]] = position;
this.weight[ position ] = this.weight[ leafCount ];
this.child[ position-- ] = this.child[ leafCount-- ];
}
this.weight[position] = weight;
this.child[position] = leafPosition + 1;
this.parent[leafPosition]
= this.parent[leafPosition + 1]
= position;
position--;
leafPosition -= 2;
}
}
/**
* ノード番号iのノードと
* ノード番号jのノードを入れ換える処理を行う。
*
* @param i 入れ換え対象のノード
* @param j 入れ換え対象のノード
*/
private void swap( int i, int j ){
if( this.child[i] < 0 ){
this.leafs[ ~this.child[i] ] = j;
}else{
this.parent[ this.child[i] ]
= this.parent[ this.child[i] - 1 ]
= j;
}
if( this.child[j] < 0 ){
this.leafs[ ~this.child[j] ] = i;
}else{
this.parent[ this.child[j] ]
= this.parent[ this.child[j] - 1 ]
= i;
}
int temp = this.child[i];
this.child[i] = this.child[j];
this.child[j] = temp;
temp = this.weight[i];
this.weight[i] = this.weight[j];
this.weight[j] = temp;
}
}
//end of DynamicHuffman.java
jp/gr/java_conf/dangan/util/lha/HashAndBinaryTreeSearch.java 100644 0 0 72741 7523340000 21415 0 ustar 0 0 //start of HashAndBinaryTreeSearch.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* HashAndBinaryTreeSearch.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import jp.gr.java_conf.dangan.lang.reflect.Factory;
import jp.gr.java_conf.dangan.util.lha.HashShort;
import jp.gr.java_conf.dangan.util.lha.HashMethod;
import jp.gr.java_conf.dangan.util.lha.LzssOutputStream;
import jp.gr.java_conf.dangan.util.lha.LzssSearchMethod;
//import exceptions
import java.io.IOException;
import java.lang.NoSuchMethodException;
import java.lang.ClassNotFoundException;
import java.lang.InstantiationException;
import java.lang.reflect.InvocationTargetException;
import java.lang.Error;
import java.lang.NoSuchMethodError;
import java.lang.InstantiationError;
import java.lang.NoClassDefFoundError;
/**
* ハッシュと二分木を使った LzssSearchMethod の実装。
*
* データ圧縮ハンドブック[改定第二版]
* M.ネルソン/J.-L.ゲィリー 著
* 萩原剛志・山口英 訳
* ISBN4-8101-8605-9
* 5728円(税抜き,当方の購入当時の価格)
*
* を参考にした。
* 二分木では、最長一致を見つけることはできるが、
* 最も近い一致を見つけられるとは限らないため、
* LZSSで 一致位置が近い場所に偏る事を
* 利用するような -lh5- のような圧縮法では、
* 圧縮率はいくらか低下する。
*
*
* -- revision history --
* $Log: HashAndBinaryTreeSearch.java,v $
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [change]
* LzssSearchMethod のインタフェイス変更にあわせてインタフェイス変更
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class HashAndBinaryTreeSearch implements LzssSearchMethod{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// private static final int UNUSED
// private static final int ROOT_NODE
//------------------------------------------------------------------
/**
* 使用されていない事を示す値。
* parent[node] に UNUSED がある場合は node は未使用のnodeである。
* small[node], large[node] に UNUSED がある場合は
* node がそちら側の子ノードを持たない無い事を示す。
*/
private static final int UNUSED = -1;
/**
* 二分木の根を示す値。
* parent[node] に ROOT_NODE がある場合は node は二分木の根である。
*/
private static final int ROOT_NODE = -2;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private int DictionarySize
// private int MaxMatch
// private int Threshold
//------------------------------------------------------------------
/**
* LZSS辞書サイズ。
*/
private int DictionarySize;
/**
* LZSS圧縮に使用される値。
* 最大一致長を示す。
*/
private int MaxMatch;
/**
* LZSS圧縮に使用される閾値。
* 一致長が この値以上であれば、圧縮コードを出力する。
*/
private int Threshold;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// text buffer
//------------------------------------------------------------------
// private byte[] TextBuffer
// private int DictionaryLimit
//------------------------------------------------------------------
/**
* LZSS圧縮を施すためのバッファ。
* 前半は辞書領域、
* 後半は圧縮を施すためのデータの入ったバッファ。
* LzssSearchMethodの実装内では読み込みのみ許される。
*/
private byte[] TextBuffer;
/**
* 辞書の限界位置。
* TextBuffer前半の辞書領域にデータが無い場合に
* 辞書領域にある不定のデータ(Javaでは0)を使用
* して圧縮が行われるのを抑止する。
*/
private int DictionaryLimit;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// hash
//------------------------------------------------------------------
// private HashMethod hashMethod
// private int[] hashTable
//------------------------------------------------------------------
/**
* ハッシュ関数
*/
private HashMethod hashMethod;
/**
* ハッシュテーブル
* 添字はハッシュ値、内容は個々のハッシュ値を持つ
* 二分木の根のデータパタンの開始位置。
*/
private int[] hashTable;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// binary tree
//------------------------------------------------------------------
// private int[] parent
// private int[] small
// private int[] large
// private int[] dummy
//------------------------------------------------------------------
/**
* 親のデータパタンの開始位置を示す。
* 添え字はノード番号、内容は親ノードのデータパタンの開始位置
*/
private int[] parent;
/**
* 小さい子のデータパタンの開始位置を示す。
* 添え字はノード番号、内容は小さい子ノードデータパタンの開始位置
*/
private int[] small;
/**
* 大きい子のデータパタンの開始位置を示す。
* 添え字はノード番号、内容は大きい子ノードデータパタンの開始位置
*/
private int[] large;
/**
* slide 用のバッファ
*/
private int[] dummy;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private HashAndBinaryTreeSearch()
// public HashAndBinaryTreeSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer )
// public HashAndBinaryTreeSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer,
// String HashMethodClassName )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可
*/
private HashAndBinaryTreeSearch(){ }
/**
* ハッシュと二分木を使用した検索機構を構築する。
* ハッシュ関数はデフォルトのものを使用する。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最長一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
*/
public HashAndBinaryTreeSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer ){
this( DictionarySize,
MaxMatch,
Threshold,
TextBuffer,
HashShort.class.getName() );
}
/**
* ハッシュと二分木を使用した LzssSearchMethod を構築する。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最長一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
* @param HashMethodClassName Hash関数を提供するクラス名
*
* @exception NoClassDefFoundError
* HashMethodClassName で与えられたクラスが
* 見つからない場合。
* @exception InstantiationError
* HashMethodClassName で与えられたクラスが
* abstract class であるためインスタンスを生成できない場合。
* @exception NoSuchMethodError
* HashMethodClassName で与えられたクラスが
* コンストラクタ HashMethod( byte[] )
* を持たない場合
*/
public HashAndBinaryTreeSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer,
String HashMethodClassName ){
this.DictionarySize = DictionarySize;
this.MaxMatch = MaxMatch;
this.Threshold = Threshold;
this.TextBuffer = TextBuffer;
this.DictionaryLimit = this.DictionarySize;
try{
this.hashMethod = (HashMethod)Factory.createInstance(
HashMethodClassName,
new Object[]{ TextBuffer } );
}catch( ClassNotFoundException exception ){
throw new NoClassDefFoundError( exception.getMessage() );
}catch( InvocationTargetException exception ){
throw new Error( exception.getTargetException().getMessage() );
}catch( NoSuchMethodException exception ){
throw new NoSuchMethodError( exception.getMessage() );
}catch( InstantiationException exception ){
throw new InstantiationError( exception.getMessage() );
}
// ハッシュテーブルの初期化
this.hashTable = new int[ this.hashMethod.tableSize() ];
for( int i = 0 ; i < this.hashTable.length ; i++ ){
this.hashTable[i] = HashAndBinaryTreeSearch.UNUSED;
}
// 二分木の初期化
this.parent = new int[ DictionarySize ];
this.large = new int[ DictionarySize ];
this.small = new int[ DictionarySize ];
for( int i = 0 ; i < this.parent.length ; i++ ){
this.parent[i] = HashAndBinaryTreeSearch.UNUSED;
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.LzssSearchMethod
//------------------------------------------------------------------
// public void put( int position )
// public int searchAndPut( int position )
// public int search( int position, int lastPutPos )
// public void slide( int slideWidth, int slideEnd )
// public int putRequires()
//------------------------------------------------------------------
/**
* position から始まるデータパタンを
* ハッシュと二分木を使用した検索機構に登録する。
*
* @param position TextBuffer内のデータパタンの開始位置
*/
public void put( int position ){
//------------------------------------------------------------------
// 二分木から最も古いデータパタンを削除
this.deleteNode( position - this.DictionarySize );
//------------------------------------------------------------------
// 二分木から position を挿入する位置を検索
int hash = this.hashMethod.hash( position );
int parentpos = this.hashTable[ hash ];
int scanpos = this.hashTable[ hash ];
byte[] buf = this.TextBuffer;
int max = position + this.MaxMatch;
int p = 0;
int s = 0;
while( scanpos != HashAndBinaryTreeSearch.UNUSED ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ){
//完全一致を発見
this.replaceNode( scanpos, position );
return;
}
}
parentpos = scanpos;
scanpos = ( buf[ s ] < buf[ p ]
? this.large[ scanpos & ( this.DictionarySize - 1 ) ]
: this.small[ scanpos & ( this.DictionarySize - 1 ) ] );
}
//------------------------------------------------------------------
// position から始まるデータパタンを 二分木に登録
if( this.hashTable[ hash ] != HashAndBinaryTreeSearch.UNUSED ){
this.addNode( parentpos, position, p - position );
}else{
this.hashTable[ hash ] = position;
int node = position & ( this.DictionarySize - 1 );
this.parent[ node ] = HashAndBinaryTreeSearch.ROOT_NODE;
this.small[ node ] = HashAndBinaryTreeSearch.UNUSED;
this.large[ node ] = HashAndBinaryTreeSearch.UNUSED;
}
}
/**
* ハッシュと二分木を使用した検索機構に登録された
* データパタンから position から始まるデータパタンと
* 最長の一致を持つものを検索し、
* 同時に position から始まるデータパタンを
* ハッシュと二分木を使用した検索機構に登録する。
*
* @param position TextBuffer内のデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int searchAndPut( int position ){
//------------------------------------------------------------------
// 二分木から最も古いデータパタンを削除
this.deleteNode( position - this.DictionarySize );
//------------------------------------------------------------------
// 二分木から最長一致を検索
int hash = this.hashMethod.hash( position );
int matchlen = -1;
int matchpos = this.hashTable[ hash ];
int parentpos = this.hashTable[ hash ];
int scanpos = this.hashTable[ hash ];
byte[] buf = this.TextBuffer;
int max = position + this.MaxMatch;
int p = 0;
int s = 0;
int len = 0;
while( scanpos != HashAndBinaryTreeSearch.UNUSED ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ){
//完全一致を発見
this.replaceNode( matchpos, position );
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
}else if( matchlen == len && matchpos < scanpos ){
matchpos = scanpos;
}
parentpos = scanpos;
scanpos = ( buf[ s ] < buf[ p ]
? this.large[ scanpos & ( this.DictionarySize - 1 ) ]
: this.small[ scanpos & ( this.DictionarySize - 1 ) ] );
}
//------------------------------------------------------------------
// position から始まるデータパタンを 二分木に登録
if( this.hashTable[ hash ] != HashAndBinaryTreeSearch.UNUSED ){
this.addNode( parentpos, position, len );
}else{
this.hashTable[ hash ] = position;
int node = position & ( this.DictionarySize - 1 );
this.parent[ node ] = HashAndBinaryTreeSearch.ROOT_NODE;
this.small[ node ] = HashAndBinaryTreeSearch.UNUSED;
this.large[ node ] = HashAndBinaryTreeSearch.UNUSED;
}
//------------------------------------------------------------------
// メソッドの先頭で削除された
// 最も遠いデータパタンと比較
scanpos = position - this.DictionarySize;
if( this.DictionaryLimit <= scanpos ){
len = 0;
while( this.TextBuffer[ scanpos + len ]
== this.TextBuffer[ position + len ] ){
if( this.MaxMatch <= ++len ) break;
}
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
}
}
//------------------------------------------------------------------
// 最長一致を呼び出し元に返す。
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* ハッシュと二分木を使用した検索機構に登録されたデータパタンを検索し
* position から始まるデータパタンと最長の一致を持つものを得る。
* TextBuffer.length < position + MaxMatch となるような position では、
* 二分木を完全に走査しないため最長一致を得られるとは限らない。
*
* @param position TextBuffer内のデータパタンの開始位置。
* @param lastPutPos 最後に登録したデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int search( int position, int lastPutPos ){
//------------------------------------------------------------------
// 二分木に登録されていないデータパタンを
// 単純な逐次検索で検索する。
int matchlen = this.Threshold - 1;
int matchpos = position;
int scanpos = position - 1;
int scanlimit = Math.max( this.DictionaryLimit, lastPutPos );
byte[] buf = this.TextBuffer;
int max = Math.min( this.TextBuffer.length,
position + this.MaxMatch );
int s = 0;
int p = 0;
int len = 0;
while( scanlimit < scanpos ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
}
scanpos--;
}
//------------------------------------------------------------------
// 二分木を探索
if( this.hashMethod.hashRequires() <= this.TextBuffer.length - position ){
int hash = this.hashMethod.hash( position );
scanpos = this.hashTable[ hash ];
scanlimit = Math.max( this.DictionaryLimit,
position - this.DictionarySize );
while( scanpos != HashAndBinaryTreeSearch.UNUSED ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
if( p < max ){
len = p - position;
if( scanlimit <= scanpos ){
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
}else if( matchlen == len && matchpos < scanpos ){
matchpos = scanpos;
}
}
scanpos = ( buf[ s ] < buf[ p ]
? this.large[ scanpos & ( this.DictionarySize - 1 ) ]
: this.small[ scanpos & ( this.DictionarySize - 1 ) ] );
}else{
break;
}
}
}
//------------------------------------------------------------------
// 最長一致を呼び出し元に返す。
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* TextBuffer内の position までのデータを前方へ移動する際、
* それに応じて ハッシュと二分木を使用した検索機構を構成するデータも
* TextBuffer内のデータと矛盾しないように前方へ移動する処理を行う。
*/
public void slide(){
this.DictionaryLimit = Math.max( 0, this.DictionaryLimit - this.DictionarySize );
this.slideTree( this.hashTable );
this.slideTree( this.parent );
this.slideTree( this.small );
this.slideTree( this.large );
}
/**
* put() または searchAndPut() を使用して
* データパタンを二分木に登録する際に
* 必要とするデータ量を得る。
* 二分木は登録の際にデータパタンを構成する
* 全て(MaxMatchバイト)のデータを必要とする。
*
* @return コンストラクタで与えた MaxMatch
*/
public int putRequires(){
return this.MaxMatch;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// node operation
//------------------------------------------------------------------
// private void addNode( int addpos, int position, int len )
// private void deleteNode( int position )
// private void contractNode( int oldpos, int newpos )
// private void replaceNode( int oldpos, int newpos )
//------------------------------------------------------------------
/**
* parentpos のデータパタンの子として
* position から始まるデータパタンを二分木に登録する。
* parentpos と position のデータパタンは len バイト一致する。
* position の位置のノードはあらかじめ deleteNode 等で
* UNUSED の状態にしておくこと。
*
* @param parentpos 親のデータパタンのTextBuffer内の開始位置
* @param position 新規追加するデータパタンのTextBuffer内の開始位置
* @param len 親のデータパタンと新規追加するデータパタンの一致長
*/
private void addNode( int parentpos, int position, int len ){
int parentnode = parentpos & ( this.DictionarySize - 1 );
int node = position & ( this.DictionarySize - 1 );
if( this.TextBuffer[ parentpos + len ] < this.TextBuffer[ position + len ] ){
this.large[ parentnode ] = position;
}else{
this.small[ parentnode ] = position;
}
this.parent[ node ] = parentpos;
this.small[ node ] = HashAndBinaryTreeSearch.UNUSED;
this.large[ node ] = HashAndBinaryTreeSearch.UNUSED;
}
/**
* position から始まるデータパタンを二分木から削除する。
*
* @param position 削除するデータパタンの開始位置
*/
private void deleteNode( int position ){
int node = position & ( this.DictionarySize - 1 );
if( this.parent[ node ] != HashAndBinaryTreeSearch.UNUSED ){
if( this.small[ node ] == HashAndBinaryTreeSearch.UNUSED
&& this.large[ node ] == HashAndBinaryTreeSearch.UNUSED ){
this.contractNode( position, HashAndBinaryTreeSearch.UNUSED );
}else if( this.small[ node ] == HashAndBinaryTreeSearch.UNUSED ){
this.contractNode( position, this.large[ node ] );
}else if( this.large[ node ] == HashAndBinaryTreeSearch.UNUSED ){
this.contractNode( position, this.small[ node ] );
}else{
int replace = this.findNext( position );
this.deleteNode( replace );
this.replaceNode( position, replace );
}
}
}
/**
* 子に newpos しか持たない oldpos を, newpos で置き換える。
* oldpos は二分木から削除される。
*
* @param oldpos 削除するデータパタンの開始位置
* @param newpos oldposに置き換わるデータパタンの開始位置
*/
private void contractNode( int oldpos, int newpos ){
int oldnode = oldpos & ( this.DictionarySize - 1 );
int newnode = newpos & ( this.DictionarySize - 1 );
int parentpos = this.parent[ oldnode ];
int parentnode = parentpos & ( this.DictionarySize - 1 );
if( parentpos != HashAndBinaryTreeSearch.ROOT_NODE ){
if( oldpos == this.small[ parentnode ] ){
this.small[ parentnode ] = newpos;
}else{
this.large[ parentnode ] = newpos;
}
}else{
this.hashTable[ this.hashMethod.hash( oldpos ) ] = newpos;
}
if( newpos != HashAndBinaryTreeSearch.UNUSED ){
this.parent[ newnode ] = parentpos;
}
this.parent[ oldnode ] = HashAndBinaryTreeSearch.UNUSED;
}
/**
* oldpos を二分木に含まれない新しいデータパタン newpos で置き換える。
* newpos が二分木に含まれているような場合には、
* いったんdeleteNode(newpos) するなどして、
* 二分木から外す必要がある。
* oldpos は二分木から削除される。
*
* @param oldpos 削除するデータパタンの開始位置
* @param newpos oldposに置き換わるデータパタンの開始位置
*/
private void replaceNode( int oldpos, int newpos ){
int oldnode = oldpos & ( this.DictionarySize - 1 );
int newnode = newpos & ( this.DictionarySize - 1 );
int parentpos = this.parent[ oldnode ];
int parentnode = parentpos & ( this.DictionarySize - 1 );
if( parentpos != HashAndBinaryTreeSearch.ROOT_NODE ){
if( oldpos == this.small[ parentnode ] ){
this.small[ parentnode ] = newpos;
}else{
this.large[ parentnode ] = newpos;
}
}else{
this.hashTable[ this.hashMethod.hash( oldpos ) ] = newpos;
}
this.parent[ newnode ] = parentpos;
this.small[ newnode ] = this.small[ oldnode ];
this.large[ newnode ] = this.large[ oldnode ];
if( this.small[ newnode ] != HashAndBinaryTreeSearch.UNUSED ){
this.parent[ this.small[ newnode ] & ( this.DictionarySize - 1 ) ] = newpos;
}
if( this.large[ newnode ] != HashAndBinaryTreeSearch.UNUSED ){
this.parent[ this.large[ newnode ] & ( this.DictionarySize - 1 ) ] = newpos;
}
this.parent[ oldnode ] = HashAndBinaryTreeSearch.UNUSED;
this.large[ oldnode ] = HashAndBinaryTreeSearch.UNUSED;
this.small[ oldnode ] = HashAndBinaryTreeSearch.UNUSED;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// private int findNext( int position )
// private void slideTree( int[] src, int[] dst, int start, int end, int width )
//------------------------------------------------------------------
/**
* deleteNode( position ) したときに、
* small と large の両方の葉が見つかった場合、
* position のから始まるデータパタンと
* 置き換えるべき データパタンの開始位置を探し出す。
*
* @param position 置き換えられるデータパタンの開始位置
*
* @return position のから始まるデータパタンと
* 置き換えるべき データパタンの開始位置
*/
private int findNext( int position ){
int node = position & ( this.DictionarySize - 1 );
position = this.small[ node ];
node = position & ( this.DictionarySize - 1 );
while( HashAndBinaryTreeSearch.UNUSED != this.large[ node ] ){
position = this.large[ node ];
node = position & ( this.DictionarySize - 1 );
}
return position;
}
/**
* slide() 時に、二分木の各要素を移動させるために使用する。
*
* @param array 走査する配列
*/
private void slideTree( int[] array ){
for( int i = 0 ; i < array.length ; i++ ){
array[ i ] = ( 0 <= array[i]
? array[i] - this.DictionarySize
: array[i] );
}
}
}
//end of HashAndBinaryTreeSearch.java
jp/gr/java_conf/dangan/util/lha/HashAndChainedListSearch.java 100644 0 0 71300 7523340000 21526 0 ustar 0 0 //start of HashAndChainedListSearch.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* HashAndChainedListSearch.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import jp.gr.java_conf.dangan.lang.reflect.Factory;
import jp.gr.java_conf.dangan.util.lha.HashMethod;
import jp.gr.java_conf.dangan.util.lha.HashDefault;
import jp.gr.java_conf.dangan.util.lha.LzssOutputStream;
import jp.gr.java_conf.dangan.util.lha.LzssSearchMethod;
//import exceptions
import java.io.IOException;
import java.lang.NoSuchMethodException;
import java.lang.ClassNotFoundException;
import java.lang.InstantiationException;
import java.lang.IllegalArgumentException;
import java.lang.reflect.InvocationTargetException;
import java.lang.Error;
import java.lang.NoSuchMethodError;
import java.lang.InstantiationError;
import java.lang.NoClassDefFoundError;
/**
* ハッシュと単方向連結リストを使って高速化された LzssSearchMethod。
* 検索を打ち切ることによる高速化も行っているため、
* 必ず最長一致を見つけることが出来るとは限らない。
*
*
* -- revision history --
* $Log: HashAndChainedListSearch.java,v $
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [change]
* LzssSearchMethod のインタフェイス変更にあわせてインタフェイス変更
* [improvement]
* ar940528 の TEST5相当 の実装に変更。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class HashAndChainedListSearch implements LzssSearchMethod{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private int DictionarySize
// private int MaxMatch
// private int Threshold
//------------------------------------------------------------------
/**
* LZSS辞書サイズ。
*/
private int DictionarySize;
/**
* LZSS圧縮に使用される値。
* 最大一致長を示す。
*/
private int MaxMatch;
/**
* LZSS圧縮に使用される閾値。
* 一致長が この値以上であれば、圧縮コードを出力する。
*/
private int Threshold;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// text buffer
//------------------------------------------------------------------
// private byte[] TextBuffer
// private int DictionaryLimit
//------------------------------------------------------------------
/**
* LZSS圧縮を施すためのバッファ。
* 前半は辞書領域、
* 後半は圧縮を施すためのデータの入ったバッファ。
* LzssSearchMethodの実装内では読み込みのみ許される。
*/
private byte[] TextBuffer;
/**
* 辞書の限界位置。
* TextBuffer前半の辞書領域にデータが無い場合に
* 辞書領域にある不定のデータ(Javaでは0)を使用
* して圧縮が行われるのを抑止する。
*/
private int DictionaryLimit;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// hash
//------------------------------------------------------------------
// private HashMethod hashMethod
// private int[] hashTable
// private char[] tooBigFlag
//------------------------------------------------------------------
/**
* ハッシュ関数
*/
private HashMethod hashMethod;
/**
* ハッシュテーブル
* 添字はハッシュ値、内容はTextBuffer内の位置
*/
private int[] hashTable;
/**
* 同じハッシュ値を持つデータパタンの連結リストの長さが
* 一定以上になった場合にセットするフラグ。
*
* boolean[] にすると何故か遅くなるので
* char[] として 16個纏めて扱う。
* 扱う場合は ユーティリティメソッド
* isTooBig(), setTooBigFlag(), clearTooBigFlag() を介して扱う。
*/
private char[] tooBigFlag;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// cahined list
//------------------------------------------------------------------
// private int[] prev
// private int SearchLimitCount
//------------------------------------------------------------------
/**
* 同じハッシュ値を持つデータパタン開始位置を持つ
* 単方向連結リスト。
*/
private int[] prev;
/**
* 探索試行回数の上限値を持つ。
* この回数以上の探索は行わない。
*/
private int SearchLimitCount;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private HashAndChainedListSearch()
// public HashAndChainedListSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer )
// public HashAndChainedListSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer,
// int SearchLimitCount )
// public HashAndChainedListSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer,
// String HashMethodClassName )
// public HashAndChainedListSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer,
// String HashMethodClassName,
// int SearchLimitCount )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private HashAndChainedListSearch(){ }
/**
* ハッシュと連結リストを使用した LzssSearchMethod を構築する。
* ハッシュ関数と探索試行回数の上限値にはデフォルトのものが使用される。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最長一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
*/
public HashAndChainedListSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer ){
this( DictionarySize,
MaxMatch,
Threshold,
TextBuffer,
HashDefault.class.getName(),
256 );
}
/**
* ハッシュと連結リストを使用した LzssSearchMethod を構築する。
* ハッシュ関数にはデフォルトのものが使用される。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最長一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
* @param SearchLimitCount 探索試行回数の上限
*
* @exception IllegalArgumentException
* SearchLimitCount が0以下の場合
*/
public HashAndChainedListSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer,
int SearchLimitCount ){
this( DictionarySize,
MaxMatch,
Threshold,
TextBuffer,
HashDefault.class.getName(),
SearchLimitCount );
}
/**
* ハッシュと連結リストを使用した LzssSearchMethod を構築する。
* 探索試行回数の上限値にはデフォルトのものが使用される。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最長一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
* @param HashMethodClassName Hash関数を提供するクラス名
*
* @exception NoClassDefFoundError
* HashMethodClassName で与えられたクラスが
* 見つからない場合。
* @exception InstantiationError
* HashMethodClassName で与えられたクラスが
* abstract class であるためインスタンスを生成できない場合。
* @exception NoSuchMethodError
* HashMethodClassName で与えられたクラスが
* コンストラクタ HashMethod( byte[] )を持たない場合
*/
public HashAndChainedListSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer,
String HashMethodClassName ){
this( DictionarySize,
MaxMatch,
Threshold,
TextBuffer,
HashMethodClassName,
256 );
}
/**
* ハッシュと連結リストを使用した LzssSearchMethod を構築する。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最長一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
* @param HashMethodClassName Hash関数を提供するクラス名
* @param SearchLimitCount 探索試行回数の上限
*
* @exception IllegalArgumentException
* SearchLimitCount が0以下の場合
* @exception NoClassDefFoundError
* HashMethodClassName で与えられたクラスが
* 見つからない場合。
* @exception InstantiationError
* HashMethodClassName で与えられたクラスが
* abstract class であるためインスタンスを生成できない場合。
* @exception NoSuchMethodError
* HashMethodClassName で与えられたクラスが
* コンストラクタ HashMethod( byte[] )を持たない場合
*/
public HashAndChainedListSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer,
String HashMethodClassName,
int SearchLimitCount ){
if( 0 < SearchLimitCount ){
this.DictionarySize = DictionarySize;
this.MaxMatch = MaxMatch;
this.Threshold = Threshold;
this.TextBuffer = TextBuffer;
this.DictionaryLimit = this.DictionarySize;
this.SearchLimitCount = SearchLimitCount;
try{
this.hashMethod = (HashMethod)Factory.createInstance(
HashMethodClassName,
new Object[]{ TextBuffer } );
}catch( ClassNotFoundException exception ){
throw new NoClassDefFoundError( exception.getMessage() );
}catch( InvocationTargetException exception ){
throw new Error( exception.getTargetException().getMessage() );
}catch( NoSuchMethodException exception ){
throw new NoSuchMethodError( exception.getMessage() );
}catch( InstantiationException exception ){
throw new InstantiationError( exception.getMessage() );
}
// ハッシュテーブル初期化
this.hashTable = new int[ this.hashMethod.tableSize() ];
for( int i = 0 ; i < this.hashTable.length ; i++ ){
this.hashTable[i] = -1;
}
// 連結リスト初期化
this.prev = new int[ this.DictionarySize ];
for( int i = 0 ; i < this.prev.length ; i++ ){
this.prev[i] = -1;
}
this.tooBigFlag = new char[ this.hashMethod.tableSize() >> 4 ];
}else{
throw new IllegalArgumentException( "SearchLimitCount must be 1 or more." );
}
}
//------------------------------------------------------------------
// method jp.gr.java_conf.dangan.util.lha.LzssSearchMethod
//------------------------------------------------------------------
// public void put( int position )
// public int searchAndPut( int position )
// public int search( int position, int lastPutPos )
// public void slide( int slideWidth, int slideEnd )
// public int putRequires()
//------------------------------------------------------------------
/**
* position から始まるデータパタンを
* ハッシュと連結リストから成る検索機構に登録する。
*
* @param position TextBuffer内のデータパタンの開始位置
*/
public void put( int position ){
int hash = this.hashMethod.hash( position );
this.prev[ position & ( this.DictionarySize - 1 ) ] = this.hashTable[ hash ];
this.hashTable[ hash ] = position;
}
/**
* ハッシュと連結リストから成る検索機構に登録された
* データパタンから position から始まるデータパタンと
* 最長の一致を持つものを検索し、
* 同時に position から始まるデータパタンを
* ハッシュと連結リストから成る検索機構に登録する。
*
* @param position TextBuffer内のデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int searchAndPut( int position ){
int matchlen = this.Threshold - 1;
int matchpos = position;
int maxmatch = this.MaxMatch;
int scanlimit = Math.max( this.DictionaryLimit,
position - this.DictionarySize );
//------------------------------------------------------------------
// 連結リストの長さが長すぎる場合 offset を使用して
// 連結リストの短いハッシュ値を使う。
int poshash = this.hashMethod.hash( position );
int offhash = poshash;
int offset = 0;
while( this.isTooBig( offhash )
&& offset < this.MaxMatch - this.hashMethod.hashRequires() ){
offset++;
offhash = this.hashMethod.hash( position + offset );
}
//------------------------------------------------------------------
// メインループ
// 最大 offhash と poshash から始まる 2つの連結リストを走査する。
byte[] buf = this.TextBuffer;
int max = position + this.MaxMatch;
int s = 0;
int p = 0;
int len = 0;
while( true ){
int scanpos = this.hashTable[ offhash ];
int searchCount = this.SearchLimitCount;
while( scanlimit <= scanpos - offset && 0 < --searchCount ){
if( buf[ scanpos + matchlen - offset ]
== buf[ position + matchlen ] ){
s = scanpos - offset;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos - offset;
matchlen = len;
if( max <= p ) break;
}
}
scanpos = this.prev[ scanpos & ( this.DictionarySize - 1 ) ];
}
if( searchCount <= 0 ){
this.setTooBigFlag( offhash );
}else if( scanpos < scanlimit ){
this.clearTooBigFlag( offhash );
}
if( 0 < offset
&& matchlen < this.hashMethod.hashRequires() + offset ){
offset = 0;
maxmatch = this.hashMethod.hashRequires() + offset - 1;
max = position + maxmatch;
offhash = poshash;
}else{
break;
}
}
//------------------------------------------------------------------
// ハッシュと連結リストを使用した検索機構に
// position から始まるデータパタンを登録する。
this.prev[ position & ( this.DictionarySize - 1 ) ] = this.hashTable[ poshash ];
this.hashTable[ poshash ] = position;
//------------------------------------------------------------------
// 最長一致を呼び出し元に返す。
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* ハッシュと連結リストを使用した検索機構に登録された
* データパタンを検索し position から始まるデータパタンと
* 最長の一致を持つものを得る。
*
* @param position TextBuffer内のデータパタンの開始位置。
* @param lastPutPos 最後に登録したデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int search( int position, int lastPutPos ){
//------------------------------------------------------------------
// ハッシュと連結リストによる検索機構に登録されていない
// データパタンを単純な逐次検索で検索する。
int matchlen = this.Threshold - 1;
int matchpos = position;
int scanpos = position - 1;
int scanlimit = Math.max( this.DictionaryLimit, lastPutPos );
byte[] buf = this.TextBuffer;
int max = Math.min( this.TextBuffer.length,
position + this.MaxMatch );
int s = 0;
int p = 0;
int len = 0;
while( scanlimit < scanpos ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
}
scanpos--;
}
//------------------------------------------------------------------
// ハッシュと連結リストを使用した検索機構 から検索する。
if( this.hashMethod.hashRequires() < this.TextBuffer.length - position ){
int maxmatch = this.MaxMatch;
scanlimit = Math.max( this.DictionaryLimit,
position - this.DictionarySize );
// 連結リストの長さが長すぎる場合 offset を使用して
// 連結リストの短いハッシュ値を使う。
int poshash = this.hashMethod.hash( position );
int offhash = poshash;
int offset = 0;
while( this.isTooBig( offhash )
&& offset < this.MaxMatch - this.hashMethod.hashRequires() ){
offset++;
offhash = this.hashMethod.hash( position + offset );
}
// メインループ
// 最大 offhash と poshash から始まる 2つの連結リストを走査する。
while(true){
int searchCount = this.SearchLimitCount;
scanpos = this.hashTable[ offhash ];
while( scanlimit <= scanpos - offset && 0 < --searchCount ){
if( buf[ scanpos + matchlen - offset ]
== buf[ position + matchlen ] ){
s = scanpos - offset;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos - offset;
matchlen = len;
if( max <= p ) break;
}
}
scanpos = this.prev[ scanpos & ( this.DictionarySize - 1 ) ];
}
if( searchCount <= 0 ){
this.setTooBigFlag( offhash );
}else if( scanpos < scanlimit ){
this.clearTooBigFlag( offhash );
}
if( 0 < offset
&& matchlen < this.hashMethod.hashRequires() + offset ){
offset = 0;
maxmatch = this.hashMethod.hashRequires() + offset - 1;
max = Math.min( this.TextBuffer.length,
position + maxmatch );
offhash = poshash;
}else{
break;
}
}
}
//------------------------------------------------------------------
// 最長一致を呼び出し元に返す。
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* TextBuffer内のpositionまでのデータを
* 前方へ移動する際、それに応じて SearchMethod内の
* データも TextBuffer内のデータと矛盾しないように
* 前方へ移動する処理を行う。
*/
public void slide(){
this.DictionaryLimit = Math.max( 0, this.DictionaryLimit - this.DictionarySize );
for( int i = 0 ; i < this.hashTable.length ; i++ ){
int pos = this.hashTable[i] - this.DictionarySize;
this.hashTable[i] = ( 0 <= pos ? pos : -1 );
}
for( int i = 0 ; i < this.prev.length ; i++ ){
int pos = this.prev[i] - this.DictionarySize;
this.prev[i] = ( 0 <= pos ? pos : -1 );;
}
}
/**
* put() で LzssSearchMethodにデータを
* 登録するときに使用されるデータ量を得る。
* HashAndChainedListSearch では、
* 内部で使用している HashMethod の実装が
* hash() のために必要とするデータ量
* ( HashMethod.hashRequires() の戻り値 ) を返す。
*
* @return 内部で使用している HashMethod の実装が
* hash() のために必要とするデータ量
*/
public int putRequires(){
return this.hashMethod.hashRequires();
}
//------------------------------------------------------------------
// method of ImprovedLzssSearchMethod
//------------------------------------------------------------------
// public int searchAndPut( int position, int[] matchposs )
//------------------------------------------------------------------
/**
* より良い LZSS 圧縮のための選択肢を提供する searchAndPut()。
* 例えば一致長 3, 一致位置 4 と 一致長 4, 一致位置 1024 では
* 一致長 3, 一致位置 4 + 非圧縮1文字 の方が出力ビット数が
* 少なくなる事がある。そのような場合に対処するため一致長毎に
* positionに一番近い一致位置を列挙する。
*
* @param position 検索対象のデータパタンの開始位置
* @param matchposs 一致位置の列挙を格納して返すための配列
* matchpos[0] には 一致長が Threshold の一致位置が、
* matchpos[1] には 一致長が Threshold + 1 の一致位置が格納される。
* 一致が見つからなかった場合には LzssOutputStream.NOMATCH が格納される。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn で生成された SearchReturn が返される。
* 一致が見つからない場合は LzssOutputStream.NOMATCH が返される。
*/
public int searchAndPut( int position, int[] matchposs ){
int matchlen = this.Threshold - 1;
int matchpos = position;
int maxmatch = this.MaxMatch;
int scanlimit = Math.max( this.DictionaryLimit,
position - this.DictionarySize );
int searchCount = this.SearchLimitCount;
for( int i = 0 ; i < matchposs.length ; i++ )
matchposs[i] = LzssOutputStream.NOMATCH;
int scanpos = this.hashTable[ this.hashMethod.hash( position ) ];
while( scanlimit < scanpos && 0 < searchCount-- ){
if( this.TextBuffer[ scanpos + matchlen ]
== this.TextBuffer[ position + matchlen ] ){
int len = 0;
while( this.TextBuffer[ scanpos + len ]
== this.TextBuffer[ position + len ] ){
if( maxmatch <= ++len ) break;
}
if( matchlen < len ){
int i = matchlen + 1 - this.Threshold;
int end = Math.min( len + 1 - this.Threshold, matchposs.length );
while( i < end ) matchposs[ i++ ] = scanpos;
matchpos = scanpos;
matchlen = len;
if( maxmatch <= len )
break;
}
}
scanpos = this.prev[ scanpos & ( this.DictionarySize - 1 ) ];
}
this.put( position );
if( matchpos < position )
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
else
return LzssOutputStream.NOMATCH;
}
//------------------------------------------------------------------
// local methods
//------------------------------------------------------------------
// too big flag
//------------------------------------------------------------------
// private int isTooBig( int hash )
// private void setTooBigFlag( int hash )
// private void clearTooBigFlag( int hash )
//------------------------------------------------------------------
/**
* hash の連結リストが閾値を超えているかを得る。
*
* @param hash ハッシュ値
*
* @return 連結リストの長さが閾値を超えているなら true
* 超えていなければ false
*/
private boolean isTooBig( int hash ){
return 0 != ( this.tooBigFlag[ hash >> 4 ] & ( 1 << ( hash & 0x0F ) ) );
}
/**
* hash の連結リストが閾値を超えた事を示す
* フラグをセットする。
*
* @param hash too big フラグをセットするハッシュ値
*/
private void setTooBigFlag( int hash ){
this.tooBigFlag[ hash >> 4 ] |= 1 << ( hash & 0x0F );
}
/**
* hash の連結リストが閾値を超えている事を示す
* フラグをクリアする。
*
* @param hash too big フラグをクリアするハッシュ値
*/
private void clearTooBigFlag( int hash ){
this.tooBigFlag[ hash >> 4 ] &= ~( 1 << ( hash & 0x0F ) );
}
}
//end of HashAndChainedListSearch.java
jp/gr/java_conf/dangan/util/lha/HashDefault.java 100644 0 0 12302 7523340000 17147 0 ustar 0 0 //start of HashDefault.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* HashDefault.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import jp.gr.java_conf.dangan.util.lha.HashMethod;
//import exceptions
/**
* 試作プログラム ar940528 や LHa for Unix で使用されているハッシュ関数。
* gzip で使用されているを参考にしたようだ。
*
*
* -- revision history --
* $Log: HashDefault.java,v $
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [change]
* HashMethod のインタフェイス変更にあわせてインテフェイス変更。
* コンストラクタで引数チェックを削除。
* [maintanance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class HashDefault implements HashMethod{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// private static final int HashMask
//------------------------------------------------------------------
/**
* ハッシュ値を生成するためのマスク値
*/
private static final int HashMask = 0x7FFF;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private byte[] TextBuffer
//------------------------------------------------------------------
/**
* LZSS圧縮を施すためのバッファ。
* 前半は辞書領域、
* 後半は圧縮を施すためのデータの入ったバッファ。
* HashMethodの実装内では Hash値の生成のための読み込みにのみ使用する。
*/
private byte[] TextBuffer;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private HashDefault()
// public HashDefault( byte[] TextBuffer )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可
*/
private HashDefault(){ }
/**
* ar940528 や LHa for Unix で使用されているハッシュ関数を構築する。
*
* @param TextBuffer LZSS圧縮用のバッファ。
* Hash値生成のため読み込み用に使用する。
*/
public HashDefault( byte[] TextBuffer ){
this.TextBuffer = TextBuffer;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.HashMethod
//------------------------------------------------------------------
// public int hash( int position )
// public int hashRequires()
// public int tableSize()
//------------------------------------------------------------------
/**
* ハッシュ関数。
* コンストラクタで渡された TextBuffer の position からの
* データパタンの hash値を生成する。
*
* @param position データパタンの開始位置
*
* @return ハッシュ値
*/
public int hash( int position ){
return ( ( ( this.TextBuffer[ position ] << 5 )
^ ( this.TextBuffer[ position + 1 ] & 0xFF ) )
<< 5 ^( this.TextBuffer[ position + 2 ] & 0xFF ) )
& HashDefault.HashMask;
}
/**
* ハッシュ関数がハッシュ値を生成するために使用するバイト数を得る。
* このハッシュ関数は 3バイトのデータから
* シフトとXORを使用してハッシュ値を生成するため、
* このメソッドは常に 3 を返す。
*
* @return 常に 3
*/
public int hashRequires(){
return 3;
}
/**
* ハッシュテーブルのサイズを得る。
* このハッシュ関数は 0x0000 〜 0x7FFF のハッシュ値を生成するため、
* このメソッドは常に 0x8000(32768) を返す。
*
* @return 常に 0x8000(32768)
*/
public int tableSize(){
return 0x8000;
}
}
//end of HashDefault.java
jp/gr/java_conf/dangan/util/lha/HashMethod.java 100644 0 0 6752 7523340000 16777 0 ustar 0 0 //start of HashMethod.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* HashMethod.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
//import exceptions
/**
* ハッシュ関数を提供するインターフェイス。
*
* コンストラクタの形式は
*
* HashMethod( byte[] TextBuffer )
*
* パラメータ:
* TextBuffer - LZSS圧縮を施すデータの入ったバッファ
*
* のような形式に則ること。
* また、追加の引数をとりたい場合は
*
* HashMethod( byte[] TextBuffer,
* Object ExtraData1,
* Object ExtraData2 )
*
* のような形式を用いる。
* なお、コンストラクタの引数チェックは追加の引数がある場合について行えばよい。
*
*
* -- revision history --
* $Log: HashMethod.java,v $
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version cotrol
* [change]
* requiredSize() を hashRequires() に名前変更。
* size() を tableSize() 名前変更。
* [maintanance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public interface HashMethod{
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// public abstract int hash( int position )
// public abstract int hashRequires()
// public abstract int tableSize()
//------------------------------------------------------------------
/**
* ハッシュ関数。
* コンストラクタで渡された TextBuffer 内の
* position からのデータパタンの hash値を生成する。
*
* @param position データパタンの開始位置
*
* @return ハッシュ値
*/
public abstract int hash( int position );
/**
* ハッシュ関数が
* ハッシュ値を生成するために使用するバイト数を得る。
*
* @return ハッシュ関数がハッシュ値を
* 生成するために使用するバイト数
*/
public abstract int hashRequires();
/**
* この HashMethod を使った場合の
* HashTable のサイズを得る。
*
* @return この HashMethod を使った場合の HashTable のサイズ
*/
public abstract int tableSize();
}
//end of HashMethod.java
jp/gr/java_conf/dangan/util/lha/HashShort.java 100644 0 0 11316 7523340000 16666 0 ustar 0 0 //start of HashShort.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* HashShort.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import jp.gr.java_conf.dangan.util.lha.HashMethod;
//import exceptions
/**
* データパタンの先頭2バイトから
* 0 〜 4095 のハッシュ値を生成するハッシュ関数。
*
*
* -- revision history --
* $Log: HashShort.java,v $
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [change]
* HashMethod のインタフェイス変更にあわせてインテフェイス変更。
* [maintanance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class HashShort implements HashMethod{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private byte[] TextBuffer
//------------------------------------------------------------------
/**
* LZSS圧縮を施すためのバッファ。
* 前半は辞書領域、
* 後半は圧縮を施すためのデータの入ったバッファ。
* HashMethodの実装内では Hash値の生成のための読み込みにのみ使用する。
*/
private byte[] TextBuffer;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private HashShort()
// public HashShort( byte[] TextBuffer )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可
*/
private HashShort(){ }
/**
* データパタンの先頭2バイトから 0x000 〜 0xFFF までの値を生成する
* ハッシュ関数を構築する。
*
* @param TextBuffer LZSS圧縮用のバッファ。
* Hash値生成のため読み込み用に使用する。
*/
public HashShort( byte[] TextBuffer ){
this.TextBuffer = TextBuffer;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.HashMethod
//------------------------------------------------------------------
// public int hash( int position )
// public int hashRequires()
// public int tableSize()
//------------------------------------------------------------------
/**
* ハッシュ関数。
* コンストラクタで渡された TextBuffer の position からの
* データパタンの hash値を生成する。
*
* @param position データパタンの開始位置
*
* @return ハッシュ値
*/
public int hash( int position ){
return ( ( ( ( this.TextBuffer[ position + 1 ] & 0x0F ) << 8 )
| ( ( this.TextBuffer[ position + 1 ] & 0xFF ) >> 4 ) )
^ ( ( this.TextBuffer[ position ] & 0xFF ) << 2 ) );
}
/**
* ハッシュ関数がハッシュ値を生成するために使用するバイト数を得る。
* このハッシュ関数はデータパタンの先頭 2 バイトのデータから
* ハッシュ値を生成するため、このメソッドは常に 2 を返す。
*
* @return 常に 2
*/
public int hashRequires(){
return 2;
}
/**
* ハッシュテーブルのサイズを得る。
* このハッシュ関数は 0x000 〜 0xFFF までのハッシュ値を生成するため
* このメソッドは常に 0x1000(4096) を返す。
*
* @return 常に 0x1000(4096)
*/
public int tableSize(){
return 0x1000;
}
}
//end of HashShort.java
jp/gr/java_conf/dangan/util/lha/LhaChecksum.java 100644 0 0 11563 7523340000 17156 0 ustar 0 0 //start of LhaChecksum.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LhaChecksum.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.util.zip.Checksum;
//import exceptions
/**
* LHAで使用される 単純な 1バイトのチェックサム値を
* 算出するためのクラス。
*
*
* -- revision history --
* $Log: LhaChecksum.java,v $
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [maintanance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class LhaChecksum implements Checksum{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private int checksum
//------------------------------------------------------------------
/**
* チェックサム値
*/
private int checksum;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public LhaChecksum()
//------------------------------------------------------------------
/**
* 新しい チェックサムクラスを作成する。
*/
public LhaChecksum(){
super();
this.reset();
}
//------------------------------------------------------------------
// method of java.util.zip.Checksum method
//------------------------------------------------------------------
// update
//------------------------------------------------------------------
// public void update( int byte8 )
// public void update( byte[] buffer )
// public void update( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* byte8 で指定した 1バイトのデータで チェックサム値を更新する。
*
* @param byte8 チェックサムを更新する1バイトのデータ
*/
public void update( int byte8 ){
this.checksum += byte8;
}
/**
* buffer で指定したバイト配列で チェックサム値を更新する。
* このメソッドは
* update( buffer, 0, buffer.length )
* と同等。
*
* @param buffer チェックサムを更新するデータを持つバイト配列
*/
public void update( byte[] buffer ){
this.update( buffer, 0, buffer.length );
}
/**
* buffer で指定したバイト配列で チェックサム値を更新する。
*
* @param buffer チェックサムを更新するデータを持つバイト配列
* @param index データの開始位置
* @param length チェックサムの更新に使うバイト数
*/
public void update( byte[] buffer, int index, int length ){
while( 0 < length-- )
this.checksum += buffer[index++];
}
//------------------------------------------------------------------
// method of java.util.zip.Checksum
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void reset()
// public long getValue()
//------------------------------------------------------------------
/**
* チェックサム値を初期値に設定しなおす。
*/
public void reset(){
this.checksum = 0;
}
/**
* チェックサム値を得る。
* チェックサム値は 1バイトの値であり、
* 0x00〜0xFFにマップされる。
*
* @return チェックサム値
*/
public long getValue(){
return this.checksum & 0xFF;
}
}
//end of LhaChecksum.java
jp/gr/java_conf/dangan/util/lha/LhaFile.java 100644 0 0 142777 7574505600 16346 0 ustar 0 0 //start of LhaFile.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LhaFile.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.lang.Math;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Enumeration;
import jp.gr.java_conf.dangan.util.lha.LhaHeader;
import jp.gr.java_conf.dangan.util.lha.LhaProperty;
import jp.gr.java_conf.dangan.util.lha.CompressMethod;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.lang.SecurityException;
import java.lang.IllegalArgumentException;
import java.util.NoSuchElementException;
import java.lang.Error;
/**
* LHA書庫ファイルからエントリデータを読み出す
* InputStreamを得るためのユーティリティクラス。
* java.util.zip.ZipFile と似た
* インターフェイスを持つように作った。
* CRC16等によるチェックは行わない。
*
*
* -- revision history --
* $Log: LhaFile.java,v $
* Revision 1.1 2002/12/08 00:00:00 dangan
* [maintenance]
* LhaConstants から CompressMethod へのクラス名の変更に合わせて修正。
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [improvement]
* エントリの管理に Hashtable を使用する事によって
* 大量のエントリを持つ書庫でエントリ開始位置を
* より速く見つけられるように改良。
* [change]
* コンストラクタから 引数に String encode を取るものを廃止、
* Properties を引数に取るものを追加。
* [maintanance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class LhaFile{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// archive file of LHA
//------------------------------------------------------------------
// private RandomAccessFile archive
// private Object LastAccessObject
// private Vector headers
// private Vector entryStart
// private Hashtable hash
// private Vector duplicate
//------------------------------------------------------------------
/**
* LHA書庫形式のデータを持つ
* RandomAccessFileのインスタンス
*/
private RandomAccessFile archive;
/**
* 最後に archive にアクセスしたオブジェクト
*/
private Object LastAccessObject;
/**
* 各エントリのヘッダを持つ LhaHeader の Vector
* headers.elementAt( index ) のヘッダを持つエントリは
* entryPoint.elementAt( index ) の位置から始まる。
*/
private Vector headers;
/**
* 各エントリの開始位置を持つ Long の Vector
* headers.elementAt( index ) のヘッダを持つエントリは
* entryPoint.elementAt( index ) の位置から始まる。
*/
private Vector entryPoint;
/**
* エントリの名前(格納ファイル名)をキーに、
* キーの名前のエントリの index を持つハッシュテーブル。
* 要素は Integer
*/
private Hashtable hash;
/**
* 同名ファイルの救出用。
* 重複した名前を持つエントリの index を持つ Vector
* 要素は Integer
*/
private Vector duplicate;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// property
//------------------------------------------------------------------
// private Properties property
//------------------------------------------------------------------
/**
* 各圧縮形式に対応した復号器の生成式等が含まれるプロパティ
*/
private Properties property;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private LhaFile()
// public LhaFile( String filename )
// public LhaFile( String filename, Properties property )
// public LhaFile( File file )
// public LhaFile( File file, Properties property )
// public LhaFile( RandomAccessFile archive )
// public LhaFile( RandomAccessFile archive, boolean rescueMode )
// public LhaFile( RandomAccessFile archive, Properties property )
// public LhaFile( RandomAccessFile archive,
// Properties property, boolean rescueMode )
// private void constructerHelper( RandomAccessFile archive,
// Properties property,
// boolean rescueMode )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 仕様不可
*/
private LhaFile(){ }
/**
* filename で指定されたファイルから書庫データを読みこむLhaFileを構築する。
* 各圧縮形式に対応した復号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param filename LHA書庫ファイルの名前
*
* @exception IOException
* 入出力エラーが発生した場合
* @exception FileNotFoundException
* ファイルが見つからない場合
* @exception SecurityException
* セキュリティマネージャがファイルの読み込みを許さない場合
*
* @see LhaProperty#getProperties()
*/
public LhaFile( String filename ) throws IOException {
Properties property = LhaProperty.getProperties();
RandomAccessFile file = new RandomAccessFile( filename, "r" ); //throws FileNotFoundException SecurityException
this.constructerHelper( file, property, false ); //After Java 1.1 throws UnsupportedEncodingException
}
/**
* filename で指定されたファイルから書庫データを読みこむLhaFileを構築する。
*
* @param filename LHA書庫ファイルの名前
* @param property 各圧縮形式に対応した復号器の生成式等が含まれるプロパティ
*
* @exception IOException
* 入出力エラーが発生した場合
* @exception FileNotFoundException
* ファイルが見つからない場合
* @exception UnsupportedEncodingException
* property.getProperty( "lha.encoding" ) で得られた
* エンコーディング名がサポートされない場合
* @exception SecurityException
* セキュリティマネージャがファイルの読み込みを許さない場合
*
* @see LhaProperty
*/
public LhaFile( String filename, Properties property ) throws IOException {
RandomAccessFile file = new RandomAccessFile( filename, "r" ); //throws FileNotFoundException SecurityException
this.constructerHelper( file, property, false ); //After Java 1.1 throws UnsupportedEncodingException
}
/**
* filename で指定されたファイルから書庫データを読みこむLhaFileを構築する。
* 各圧縮形式に対応した復号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param filename LHA書庫ファイル
*
* @exception IOException
* 入出力エラーが発生した場合
* @exception FileNotFoundException
* ファイルが見つからない場合
* @exception SecurityException
* セキュリティマネージャがファイルの読み込みを許さない場合
*
* @see LhaProperty#getProperties()
*/
public LhaFile( File filename ) throws IOException {
Properties property = LhaProperty.getProperties();
RandomAccessFile file = new RandomAccessFile( filename, "r" ); //throws FileNotFoundException SecurityException
this.constructerHelper( file, property, false ); //After Java 1.1 throws UnsupportedEncodingException
}
/**
* filename で指定されたファイルから書庫データを読みこむ LhaFile を構築する。
*
* @param filename LHA書庫ファイル
* @param property 各圧縮形式に対応した復号器の生成式等が含まれるプロパティ
*
* @exception IOException
* 入出力エラーが発生した場合
* @exception FileNotFoundException
* ファイルが見つからない場合
* @exception UnsupportedEncodingException
* property.getProperty( "lha.encoding" ) で得られた
* エンコーディング名がサポートされない場合
* @exception SecurityException
* セキュリティマネージャがファイルの読み込みを許さない場合
*
* @see LhaProperty
*/
public LhaFile( File filename, Properties property ) throws IOException {
RandomAccessFile file = new RandomAccessFile( filename, "r" ); //throws FileNotFoundException SecurityException
this.constructerHelper( file, property, false ); //After Java 1.1 throws UnsupportedEncodingException
}
/**
* file で指定されたファイルから書庫データを読みこむ LhaFile を構築する。
* 各圧縮形式に対応した復号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param file LHA書庫ファイル
*
* @exception IOException
* 入出力エラーが発生した場合
* @exception FileNotFoundException
* ファイルが見つからない場合
* @exception SecurityException
* セキュリティマネージャがファイルの読み込みを許さない場合
*
* @see LhaProperty#getProperties()
*/
public LhaFile( RandomAccessFile file ) throws IOException {
Properties property = LhaProperty.getProperties();
this.constructerHelper( file, property, false );
}
/**
* file で指定されたファイルから書庫データを読みこむ LhaFile を構築する。
* 各圧縮形式に対応した復号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param file LHA書庫ファイル
* @param rescueMode true にすると壊れた書庫のデータを
* 復旧するための復旧モードでエントリを検索する。
*
* @exception IOException
* 入出力エラーが発生した場合
* @exception FileNotFoundException
* ファイルが見つからない場合
* @exception SecurityException
* セキュリティマネージャがファイルの読み込みを許さない場合
*
* @see LhaProperty#getProperties()
*/
public LhaFile( RandomAccessFile file, boolean rescueMode )
throws IOException {
Properties property = LhaProperty.getProperties();
this.constructerHelper( file, property, rescueMode );
}
/**
* file で指定されたファイルから書庫データを読みこむ LhaFile を構築する。
*
* @param file LHA書庫ファイル
* @param property 各圧縮形式に対応した復号器の生成式等が含まれるプロパティ
*
* @exception IOException
* 入出力エラーが発生した場合
* @exception FileNotFoundException
* ファイルが見つからない場合
* @exception SecurityException
* セキュリティマネージャがファイルの読み込みを許さない場合
*
* @see LhaProperty
*/
public LhaFile( RandomAccessFile file, Properties property )
throws IOException {
this.constructerHelper( file, property, false );
}
/**
* file で指定されたファイルから書庫データを読みこむ LhaFile を構築する。
*
* @param file LHA書庫ファイル
* @param property 各圧縮形式に対応した復号器の生成式等が含まれるプロパティ
* @param rescueMode true にすると壊れた書庫のデータを
* 復旧するための復旧モードでエントリを検索する。
*
* @exception IOException
* 入出力エラーが発生した場合
* @exception FileNotFoundException
* ファイルが見つからない場合
* @exception SecurityException
* セキュリティマネージャがファイルの読み込みを許さない場合
*
* @see LhaProperty
*/
public LhaFile( RandomAccessFile file, Properties property, boolean rescueMode )
throws IOException {
this.constructerHelper( file, property, rescueMode );
}
/**
* file を走査してエントリ情報を構築する。
*
* @param file LHA書庫ファイル
* @param propety 各圧縮形式に対応した復号器の生成式等が含まれるプロパティ
* @param rescueMode true にすると壊れた書庫のデータを
* 復旧するための復旧モードでエントリを検索する。
*
* @exception IOException
* 入出力エラーが発生した場合
* @exception UnsupportedEncodingException
* encodeがサポートされない場合
*/
private void constructerHelper( RandomAccessFile file,
Properties property,
boolean rescueMode )
throws IOException {
this.headers = new Vector();
this.entryPoint = new Vector();
file.seek( 0 );
CachedRandomAccessFileInputStream archive = new CachedRandomAccessFileInputStream( file );
byte[] HeaderData = LhaHeader.getFirstHeaderData( archive );
while( null != HeaderData ){
LhaHeader header = LhaHeader.createInstance( HeaderData, property );
headers.addElement( header );
entryPoint.addElement( new Long( archive.position() ) );
if( !rescueMode ){
archive.skip( header.getCompressedSize() );
HeaderData = LhaHeader.getNextHeaderData( archive );
}else{
HeaderData = LhaHeader.getFirstHeaderData( archive );
}
}
archive.close();
this.hash = new Hashtable();
this.duplicate = new Vector();
for( int i = 0 ; i < this.headers.size() ; i++ ){
LhaHeader header = (LhaHeader)headers.elementAt(i);
if( !this.hash.containsKey( header.getPath() ) ){
this.hash.put( header.getPath(), new Integer( i ) );
}else{
this.duplicate.addElement( new Integer( i ) );
}
}
this.archive = file;
this.property = (Properties)property.clone();
}
//------------------------------------------------------------------
// original method ( on the model of java.util.zip.ZipFile )
//------------------------------------------------------------------
// get InputStream
//------------------------------------------------------------------
// public InputStream getInputStream( LhaHeader header )
// public InputStream getInputStream( String name )
// public InputStream getInputStreamWithoutExtract( LhaHeader header )
// public InputStream getInputStreamWithoutExtract( String name )
//------------------------------------------------------------------
/**
* header で指定されたエントリの
* 内容を解凍しながら読みこむ入力ストリームを得る。
*
* @param header ヘッダ
*
* @return headerで指定されたヘッダを持つエントリの
* 内容を読みこむ入力ストリーム。
* エントリが見つからない場合は null。
*/
public InputStream getInputStream( LhaHeader header ){
int index = this.getIndex( header );
if( 0 <= index ){
long start = ((Long)this.entryPoint.elementAt( index )).longValue();
long len = header.getCompressedSize();
InputStream in = new RandomAccessFileInputStream( start, len );
return CompressMethod.connectDecoder( in,
header.getCompressMethod(),
this.property,
header.getOriginalSize() );
}else{
return null;
}
}
/**
* nameで指定された名前を持つエントリの
* 内容を解凍しながら読みこむ入力ストリームを得る。
*
* @param name エントリの名前
*
* @return nameで指定された名前を持つエントリの
* 内容を解凍しながら読みこむ入力ストリーム。
* エントリが見つからない場合は null。
*/
public InputStream getInputStream( String name ){
if( this.hash.containsKey( name ) ){
int index = ((Integer)this.hash.get( name )).intValue();
LhaHeader header = (LhaHeader)this.headers.elementAt( index );
long start = ((Long)this.entryPoint.elementAt( index )).longValue();
long len = header.getCompressedSize();
InputStream in = new RandomAccessFileInputStream( start, len );
return CompressMethod.connectDecoder( in,
header.getCompressMethod(),
this.property,
header.getOriginalSize() );
}else{
return null;
}
}
/**
* headerで指定されたエントリの内容を
* 解凍せずに読みこむ入力ストリームを返す。
*
* @param header ヘッダ
*
* @return headerで指定されたエントリの内容を
* 解凍せずに読みこむ入力ストリーム。
* エントリが見つからない場合は null。
*/
public InputStream getInputStreamWithoutExtract( LhaHeader header ){
int index = this.getIndex( header );
if( 0 <= index ){
long start = ((Long)this.entryPoint.elementAt( index )).longValue();
long len = header.getCompressedSize();
return new RandomAccessFileInputStream( start, len );
}else{
return null;
}
}
/**
* nameで指定された名前を持つエントリの
* 内容を解凍せずに読みこむ入力ストリームを返す。
*
* @param name エントリの名前
*
* @return nameで指定された名前を持つエントリの
* 内容を解凍せずに読みこむ入力ストリーム。
* エントリが見つからない場合は null。
*/
public InputStream getInputStreamWithoutExtract( String name ){
if( this.hash.containsKey( name ) ){
int index = ((Integer)this.hash.get( name )).intValue();
LhaHeader header = (LhaHeader)this.headers.elementAt( index );
long start = ((Long)this.entryPoint.elementAt( index )).longValue();
long len = header.getCompressedSize();
return new RandomAccessFileInputStream( start, len );
}else{
return null;
}
}
//------------------------------------------------------------------
// original method ( on the model of java.util.zip.ZipFile )
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int size()
// public Enumeration entries()
// public LhaHeader[] getEntries()
// public void close()
//------------------------------------------------------------------
/**
* この LhaFile 内のエントリの数を得る。
*
* @return ファイル内のエントリの数
*/
public int size(){
return this.headers.size();
}
/**
* この LhaFile 内のエントリの LhaHeader の列挙子を得る。
*
* @return LhaHeader の列挙子
*
* @exception IllegalStateException
* LhaFile が close() で閉じられている場合。
*/
public Enumeration entries(){
if( this.archive != null ){
return new HeaderEnumeration();
}else{
throw new IllegalStateException();
}
}
/**
* ファイル内のエントリを列挙した配列を得る。
*
* @return ファイル内のエントリを列挙した配列
*/
public LhaHeader[] getEntries(){
LhaHeader[] headers = new LhaHeader[ this.headers.size() ];
for( int i = 0 ; i < this.headers.size() ; i++ ){
headers[i] = (LhaHeader)((LhaHeader)this.headers.elementAt( i )).clone();
}
return headers;
}
/**
* この LHA書庫ファイルを閉じる。
* その際、このLhaFileが発行した全ての
* InputStreamは強制的に閉じられる。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.archive.close();
this.archive = null;
this.LastAccessObject = null;
this.headers = null;
this.entryPoint = null;
this.hash = null;
this.property = null;
this.duplicate = null;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private int getIndex( LhaHeader target )
// private static boolean equal( LhaHeader header1, LhaHeader header2 )
//------------------------------------------------------------------
/**
* headers における target の index を得る。
*
* @param target ヘッダ
*
* @return headers 内での target の index。
* headers 内に target がない場合は -1
*/
private int getIndex( LhaHeader target ){
int index = ((Integer)this.hash.get( target.getPath() )).intValue();
LhaHeader header = (LhaHeader)this.headers.elementAt( index );
if( !LhaFile.equal( header, target ) ){
boolean match = false;
for( int i = 0 ; i < this.duplicate.size() && !match ; i++ ){
index = ((Integer)this.duplicate.elementAt( i )).intValue();
header = (LhaHeader)this.headers.elementAt( index );
if( LhaFile.equal( header, target ) ){
match = true;
}
}
if( match ){
return index;
}else{
return -1;
}
}else{
return index;
}
}
/**
* 2つの LhaHeader、header1 と header2 が同等か調べる。
*
* @param header1 検査対象のヘッダ その1
* @param header2 検査対象のヘッダ その2
*
* @return header1 と header2 が同等であれば true 違えば false
*/
private static boolean equal( LhaHeader header1, LhaHeader header2 ){
return header1.getPath().equals( header2.getPath() )
&& header1.getCompressMethod().equals( header2.getCompressMethod() )
&& header1.getLastModified().equals( header2.getLastModified() )
&& header1.getCompressedSize() == header2.getCompressedSize()
&& header1.getOriginalSize() == header2.getOriginalSize()
&& header1.getCRC() == header2.getCRC()
&& header1.getOSID() == header2.getOSID()
&& header1.getHeaderLevel() == header2.getHeaderLevel();
}
//------------------------------------------------------------------
// inner classes
//------------------------------------------------------------------
// private class RandomAccessFileInputStream
// private static class CachedRandomAccessFileInputStream
// private class EntryEnumeration
//------------------------------------------------------------------
/**
* LhaFileのarchiveの ある区間内のデータを得る InputStream。
* 複数エントリを同時に処理するための 同期処理を含む。
*/
private class RandomAccessFileInputStream extends InputStream {
//------------------------------------------------------------------
// member values
//------------------------------------------------------------------
// private long position
// private long end
// private long markPosition
//------------------------------------------------------------------
/**
* archive内の現在処理位置
*/
private long position;
/**
* archive内のこのInputStreamの読み取り限界
*/
private long end;
/**
* archive内のマーク位置
*/
private long markPosition;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public RandomAccessFileInputStream( long start, long size )
//------------------------------------------------------------------
/**
* コンストラクタ。
*
* @param start 読みこみ開始位置
* @param size データのサイズ
*/
public RandomAccessFileInputStream( long start, long size ){
this.position = start;
this.end = start + size;
this.markPosition = -1;
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
// public long skip( long length )
//------------------------------------------------------------------
/**
* archiveの現在処理位置から 1byteのデータを読み込む。
*
* @return 読みこまれた1byteのデータ
* 既に読みこみ限界に達した場合は -1
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read() throws IOException {
synchronized( LhaFile.this.archive ){
if( this.position < this.end ){
if( LhaFile.this.LastAccessObject != this )
LhaFile.this.archive.seek( this.position );
int data = LhaFile.this.archive.read();
if( 0 <= data ) this.position++;
return data;
}else{
return -1;
}
}
}
/**
* archiveの現在処理位置から bufferを満たすようにデータを読み込む。
*
* @param buffer 読みこまれたデータを格納するバッファ
*
* @return 読みこまれたバイト数
* 既に読みこみ限界に達していた場合は-1
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer ) throws IOException {
return this.read( buffer, 0, buffer.length );
}
/**
* archiveの現在処理位置から bufferのindexから始まる領域へ
* lengthバイトのデータを読み込む。
*
* @param buffer 読みこまれたデータを格納するバッファ
* @param index buffer内の読みこみ開始位置
* @param length 読みこむバイト数。
*
* @return 読みこまれたバイト数
* 既に読みこみ限界に達していた場合は-1
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer, int index, int length )
throws IOException {
synchronized( LhaFile.this.archive ){
if( this.position < this.end ){
if( LhaFile.this.LastAccessObject != this ){
LhaFile.this.archive.seek( this.position );
LhaFile.this.LastAccessObject = this;
}
length = (int)Math.min( this.end - this.position, length );
length = LhaFile.this.archive.read( buffer, index, length );
if( 0 <= length ) this.position += length;
return length;
}else{
return -1;
}
}
}
/**
* lengthバイトのデータを読み飛ばす。
*
* @param length 読み飛ばしたいバイト数
*
* @return 実際に読み飛ばされたバイト数
*/
public long skip( long length ){
synchronized( LhaFile.this.archive ){
long skiplen = Math.min( this.end - this.position, length );
this.position += skiplen;
if( LhaFile.this.LastAccessObject == this )
LhaFile.this.LastAccessObject = null;
return skiplen;
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public boolean markSupported()
// public void mark( int readLimit )
// public void reset()
//------------------------------------------------------------------
/**
* このオブジェクトがmark/resetをサポートするかを返す。
*
* @return このオブジェクトはmark/resetをサポートする。
* 常にtrue。
*/
public boolean markSupported(){
return true;
}
/**
* 現在処理位置にマークを施し次のresetで
* 現在の処理位置に戻れるようにする。
*
* @param readLimit マークの有効限界。
* このオブジェクトでは意味を持たない。
*/
public void mark( int readLimit ){
this.markPosition = this.position;
}
/**
* 最後にマークされた処理位置に戻す。
*
* @exception IOException mark()されていない場合
*/
public void reset() throws IOException {
synchronized( LhaFile.this.archive ){
if( 0 <= this.markPosition ){
this.position = this.markPosition;
}else{
throw new IOException( "not marked" );
}
if( LhaFile.this.LastAccessObject == this )
LhaFile.this.LastAccessObject = null;
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* 接続された入力ストリームからブロックしないで
* 読み込むことのできるバイト数を得る。
* RandomAccessFileInputStream では
* 読み込みは常に RandomAccessFile に対する
* アクセスを伴うため、このメソッドは常に 0 を返す。
*
* @return 常に 0
*/
public int available(){
return 0;
}
/**
* この入力ストリームを閉じ、使用していた全てのリソースを開放する。
* このメソッドは何も行わない。
*/
public void close(){
}
}
/**
* ヘッダ検索用 の RandomAccessFileInputStream。
* バッファリングと同期処理を行わない事によって高速化してある。
*/
private static class CachedRandomAccessFileInputStream extends InputStream {
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private RandomAccessFile archive
//------------------------------------------------------------------
/**
* データを供給する RandomAccessFile
*/
private RandomAccessFile archive;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// cache
//------------------------------------------------------------------
// private byte[] cache
// private int cachePosition
// private int cacheLimit
//------------------------------------------------------------------
/**
* データを蓄えるためのキャッシュ
*/
private byte[] cache;
/**
* cache内の現在処理位置
*/
private int cachePosition;
/**
* cacheの読み込み限界位置
*/
private int cacheLimit;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// backup for mark/reset
//------------------------------------------------------------------
// private boolean markPositionIsInCache
// private byte[] markCache
// private int markCachePosition
// private int markCacheLimit
// private long markPosition
//------------------------------------------------------------------
/**
* mark位置がキャッシュの範囲内にあるかを示す。
* markされたとき true に設定され、
* 次に in から キャッシュへの読み込みが
* 行われたときに false に設定される。
*/
private boolean markPositionIsInCache;
/** cacheのバックアップ用 */
private byte[] markCache;
/** cachePositionのバックアップ用 */
private int markCachePosition;
/** cacheLimitのバックアップ用 */
private int markCacheLimit;
/** position のバックアップ用 */
private long markPosition;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// public CachedRandomAccessFileInputStream()
//------------------------------------------------------------------
/**
* キャッシュを使用して 高速化した RandomAccessFileInputStream を構築する。
*
* @param file データを供給する RandomAccessFile
*/
public CachedRandomAccessFileInputStream( RandomAccessFile file ){
this.archive = file;
this.cache = new byte[ 1024 ];
this.cachePosition = 0;
this.cacheLimit = 0;
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
// public long skip( long length )
//------------------------------------------------------------------
/**
* archiveの現在処理位置から 1byteのデータを読み込む。
*
* @return 読みこまれた1byteのデータ
* 既に読みこみ限界に達した場合は -1
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read() throws IOException {
if( this.cachePosition < this.cacheLimit ){
return this.cache[ this.cachePosition++ ] & 0xFF;
}else{
this.fillCache(); //throws IOException
if( this.cachePosition < this.cacheLimit ){
return this.cache[ this.cachePosition++ ] & 0xFF;
}else{
return -1;
}
}
}
/**
* archiveの現在処理位置から bufferを満たすようにデータを読み込む。
*
* @param buffer 読みこまれたデータを格納するバッファ
*
* @return 読みこまれたバイト数
* 既に読みこみ限界に達していた場合は-1
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer ) throws IOException {
return this.read( buffer, 0, buffer.length );
}
/**
* archiveの現在処理位置から bufferのindexから始まる領域へ
* lengthバイトのデータを読み込む。
*
* @param buffer 読みこまれたデータを格納するバッファ
* @param index buffer内の読みこみ開始位置
* @param length 読みこむバイト数。
*
* @return 読みこまれたバイト数
* 既に読みこみ限界に達していた場合は-1
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer, int index, int length )
throws IOException {
final int requested = length;
while( 0 < length ){
if( this.cacheLimit <= this.cachePosition ){
this.fillCache(); //throws IOException
if( this.cacheLimit <= this.cachePosition ){
if( requested == length ){
return -1;
}else{
break;
}
}
}
int copylen = Math.min( length,
this.cacheLimit - this.cachePosition );
System.arraycopy( this.cache, this.cachePosition,
buffer, index, copylen );
index += copylen;
length -= copylen;
this.cachePosition += copylen;
}
return requested - length;
}
/**
* lengthバイトのデータを読み飛ばす。
*
* @param length 読み飛ばしたいバイト数
*
* @return 実際に読み飛ばされたバイト数
*/
public long skip( long length ) throws IOException {
final long requested = length;
if( this.cachePosition < this.cacheLimit ){
long avail = (long)this.cacheLimit - this.cachePosition;
long skiplen = Math.min( length, avail );
length -= skiplen;
this.cachePosition += (int)skiplen;
}
if( 0 < length ){
long avail = this.archive.length() - this.archive.getFilePointer();
long skiplen = Math.min( avail, length );
length -= skiplen;
archive.seek( archive.getFilePointer() + skiplen );
}
return requested - length;
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public boolean markSupported()
// public void mark( int readLimit )
// public void reset()
//------------------------------------------------------------------
/**
* このオブジェクトがmark/resetをサポートするかを返す。
*
* @return このオブジェクトはmark/resetをサポートする。
* 常にtrue。
*/
public boolean markSupported(){
return true;
}
/**
* 現在処理位置にマークを施し次のresetで
* 現在の処理位置に戻れるようにする。
*
* @param readLimit マークの有効限界。
* このオブジェクトでは意味を持たない。
*/
public void mark( int readLimit ){
try{
this.markPosition = this.archive.getFilePointer();
}catch( IOException exception ){
throw new Error( "caught IOException( " + exception.getMessage() + " ) in mark()" );
}
if( this.markCache == null ){
this.markCache = (byte[])this.cache.clone();
}else{
System.arraycopy( this.cache, 0, this.markCache, 0, this.cacheLimit );
}
this.markCacheLimit = this.cacheLimit;
this.markCachePosition = this.cachePosition;
this.markPositionIsInCache = true;
}
/**
* 最後にマークされた処理位置に戻す。
*
* @exception IOException mark()されていない場合
*/
public void reset() throws IOException {
if( this.markPositionIsInCache ){
this.cachePosition = this.markCachePosition;
}else if( this.markCache == null ){ //この条件式は未だにマークされていないことを示す。コンストラクタで markCache が null に設定されるのを利用する。
throw new IOException( "not marked." );
}else{
//in が reset() できない場合は
//最初の行の this.in.reset() で
//IOException を投げることを期待している。
this.archive.seek( this.markPosition ); //throws IOException
System.arraycopy( this.markCache, 0, this.cache, 0, this.markCacheLimit );
this.cacheLimit = this.markCacheLimit;
this.cachePosition = this.markCachePosition;
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* 接続された入力ストリームからブロックしないで
* 読み込むことのできるバイト数を得る。
*
* @return ブロックしないで読み出せるバイト数。
*/
public int available(){
return this.cacheLimit - this.cachePosition;
}
/**
* この入力ストリームを閉じ、使用していた
* 全てのリソースを開放する。
*/
public void close(){
this.archive = null;
this.cache = null;
this.cachePosition = 0;
this.cacheLimit = 0;
this.markPositionIsInCache = false;
this.markCache = null;
this.markCachePosition = 0;
this.markCacheLimit = 0;
this.markPosition = 0;
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// public long position()
//------------------------------------------------------------------
/**
* ファイル先頭を始点とする現在の読み込み位置を得る。
*
* @return 現在の読み込み位置。
*/
public long position() throws IOException {
long position = this.archive.getFilePointer();
position -= this.cacheLimit - this.cachePosition;
return position;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private void fillCache()
//------------------------------------------------------------------
/**
* 必要がある場合に、キャッシュ用バッファにデータを
* 補填しキャッシュ用バッファに必ずデータが存在する
* ことを保証するために呼ばれる。
* もし EndOfStream まで読み込まれている場合は データが
* 補填されないことによって それを示す。
*
* @exception IOException 入出力エラーが発生した場合
*/
private void fillCache() throws IOException {
this.markPositionIsInCache = false;
this.cacheLimit = 0;
this.cachePosition = 0;
//キャッシュにデータを読み込み
int read = 0;
while( 0 <= read && this.cacheLimit < this.cache.length ){
read = this.archive.read( this.cache,
this.cacheLimit,
this.cache.length - this.cacheLimit );//throws IOException
if( 0 < read ) this.cacheLimit += read;
}
}
}
/**
* LhaFile にある全ての LhaHeader を返す列挙子
*/
private class HeaderEnumeration implements Enumeration {
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private int index
//------------------------------------------------------------------
/**
* 現在処理位置
*/
private int index;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public EntryEnumeration()
//------------------------------------------------------------------
/**
* LhaFile にある全ての LhaHeader を返す列挙子を構築する。
*/
public HeaderEnumeration(){
this.index = 0;
}
//------------------------------------------------------------------
// method of java.util.Enumeration
//------------------------------------------------------------------
// public boolean hasMoreElements()
// public Object nextElement()
//------------------------------------------------------------------
/**
* 列挙子にまだ要素が残っているかを得る。
*
* @return 列挙子にまだ要素が残っているなら true
* 残っていなければ false
*
* @exception IllegalStateException
* 親の LhaFile が閉じられた場合
*/
public boolean hasMoreElements(){
if( LhaFile.this.archive != null ){
return this.index < LhaFile.this.headers.size();
}else{
throw new IllegalStateException();
}
}
/**
* 列挙子の次の要素を得る。
*
* @return 列挙子の次の要素
*
* @exception IllegalStateException
* 親の LhaFile が閉じられた場合。
* @exception NoSuchElementException
* 列挙子に要素が無い場合。
*
*/
public Object nextElement(){
if( LhaFile.this.archive != null ){
if( this.index < LhaFile.this.headers.size() ){
return ((LhaHeader)LhaFile.this.headers.elementAt( this.index++ )).clone();
}else{
throw new NoSuchElementException();
}
}else{
throw new IllegalStateException();
}
}
}
}
//end of LhaFile.java
jp/gr/java_conf/dangan/util/lha/LhaHeader.java 100644 0 0 354731 10235626506 16667 0 ustar 0 0 //start of LhaHeader.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LhaHeader.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.File;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
import java.util.Date;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Properties;
import java.lang.Cloneable;
import jp.gr.java_conf.dangan.io.LittleEndian;
import jp.gr.java_conf.dangan.util.MsdosDate;
import jp.gr.java_conf.dangan.util.lha.CRC16;
import jp.gr.java_conf.dangan.util.lha.LhaProperty;
import jp.gr.java_conf.dangan.util.lha.LhaChecksum;
import jp.gr.java_conf.dangan.util.lha.CompressMethod;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.io.UnsupportedEncodingException;
import java.lang.NullPointerException;
import java.lang.IllegalStateException;
import java.lang.IllegalArgumentException;
import java.lang.CloneNotSupportedException;
import java.lang.ArrayIndexOutOfBoundsException;
import java.lang.Error;
/**
* LHAヘッダを扱う。
* このクラスは java.util.zip パッケージでは ZipEntry と近いが、
* ヘッダの入出力のためのユーティリティ関数を持つ点が違う。
* このクラスは set系メソッドで為された方が良いチェックを
* getBytes() 時に行うように書かれている。その点は注意すること。
*
*
* -- revision history --
* $Log: LhaHeader.java,v $
* Revision 1.2.2.3 2005/05/03 07:50:30 dangan
* [bug fix]
* exportLevel1Header() で skip size のチェックがされていなかった。
*
* Revision 1.2.2.2 2005/02/02 00:57:46 dangan
* [bug fix]
* importLevelXHeader(byte[], String) でファイルサイズを int で読み込んでいたため
* 31ビット値以上のサイズのファイルを正しく扱えていなかったのを修正。
*
* Revision 1.2.2.1 2003/07/20 13:19:21 dangan
* [bug fix]
* exportDirNameExtHeader(String) で System.arraycopy の src と dest の配置が間違っていた。
*
* Revision 1.2 2002/12/08 00:00:00 dangan
* [maintenance]
* LhaConstants から CompressMethod へのクラス名の変更に合わせて修正。
*
* Revision 1.1 2002/12/05 00:00:00 dangan
* [improvement]
* 64ビットファイルサイズヘッダに対応。
* [change]
* LhaUtil.DefaultEncoding から LhaProperty.encoding を使用するように変更。
* getNextHeaderData() を getFirstHeaderData() に名前変更。
* 新しい getNextHeaderData() は呼び出された位置で
* ヘッダを発見できない場合 null を返す。
* LhaHeader を拡張したサブクラスを使用する人のための createInstance() を追加。
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [bug fix]
* setDate( null ) を許していた。
* setCompressMethod( null ) を許していた。
* exportLevel2,3Header で
* Date が 32bit の time_t の範囲外の値(負の値を含む)の場合を許していた。
* [change]
* exportHeader で ヘッダレベルが 0,1,2,3 のいずれでもない場合
* IllegalStateException を投げるように変更。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.2.2.3 $
*/
public class LhaHeader implements Cloneable{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// public static final int UNKNOWN
// public static final int NO_CRC
//------------------------------------------------------------------
/**
* 不明を意味する値。
* LhaHeader.getCRC(), LhaHeader.getCompressedSize(),
* LhaHeader.getOriginalSzie() がこの値を返した場合は
* 処理前のために、その値が不明である事を示す。
*/
public static final int UNKNOWN = -1;
/**
* CRC値が無い事を意味する値。
* レベル0ヘッダでCRC値が存在しない事を意味する。
*/
public static final int NO_CRC = -2;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// file information
//------------------------------------------------------------------
// private long OriginalSize
// private Date LastModified
// private String Path
// private int CRC
//------------------------------------------------------------------
/**
* 圧縮前サイズ。
* -1 は処理前のためサイズが不明であることを意味する。
*/
private long OriginalSize;
/**
* 最終更新日時。
* 圧縮したファイルの最終更新日時。
*/
private Date LastModified;
/**
* パス名。
* パスデリミタには java.io.File.separator を使用する。
*/
private String Path;
/**
* CRC16 の値。
* -1 は 処理前のためにCRC16値が不明である事を意味する。
* -2 は レベル0ヘッダでCRC16値が無い事を意味する。
*/
private int CRC;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// information of compressed data
//------------------------------------------------------------------
// private String Method
// private long CompressedSize
// private int HeaderLevel
// private byte OSID
//------------------------------------------------------------------
/**
* 圧縮法文字列。
*/
private String Method;
/**
* 圧縮後サイズ。
* -1 は処理前のためサイズが不明であることを意味する。
*/
private long CompressedSize;
/**
* ヘッダレベル。
* 0,1,2,3の何れか
*/
private int HeaderLevel;
/**
* ヘッダを作成した OS。
*/
private byte OSID;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// private byte[] ExtraData
// private byte Level0DosAttribute
// private Vector ExtraExtHeaders
//------------------------------------------------------------------
/**
* レベル0ヘッダもしくは レベル1ヘッダの基本ヘッダ内の
* 拡張情報があった場合、これを保存する。
*/
private byte[] ExtraData;
/**
* レベル0ヘッダにおける DOSのファイル属性を保存する。
*/
private byte Level0DosAttribute;
/**
* LhaHeaderでは読み込まない情報を持つ拡張ヘッダを保存する。
*/
private Vector ExtraExtHeaders;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private LhaHeader()
// public LhaHeader( String path )
// public LhaHeader( String path, Date date )
// public LhaHeader( byte[] HeaderData )
// public LhaHeader( byte[] HeaderData, String encode )
//------------------------------------------------------------------
/**
* LhaHeaderの各値を初期化する。
*/
private LhaHeader(){
this.Method = CompressMethod.LH5;
this.OriginalSize = LhaHeader.UNKNOWN;
this.CompressedSize = LhaHeader.UNKNOWN;
this.LastModified = null;
this.HeaderLevel = 2;
this.Path = "";
this.CRC = LhaHeader.UNKNOWN;
this.OSID = (byte)'J';
this.ExtraData = null;
this.Level0DosAttribute = 0x20;
this.ExtraExtHeaders = null;
}
/**
* path という名前を持つ LhaHeader のインスタンスを生成する。
* パスデリミタには File.separator を使用すること。
* path が パスデリミタでターミネートされている場合は
* ディレクトリであると解釈される。
*
* @param path パス名
*
* @exception IllgelArgumentException
* path が null か 空文字列のいずれかである場合
*/
public LhaHeader( String path ){
this( path, new Date( System.currentTimeMillis() ) );
}
/**
* path という名前を持ち、最終更新日時が date の
* LhaHeader のインスタンスを生成する。
* パスデリミタには File.separator を使用すること。
* path が パスデリミタでターミネートされている場合は
* ディレクトリであると解釈される。
*
* @param path パス名
* @param date 最終更新日時
*
* @exception IllgelArgumentException
* path が null か 空文字列のいずれかであるか、
* date が nullである場合。
*/
public LhaHeader( String path, Date date ){
this();
if( path != null && !path.equals( "" ) && date != null ){
if( path.endsWith( File.separator ) ){
this.Method = CompressMethod.LHD;
}
this.Path = path;
this.LastModified = date;
}else if( path == null ){
throw new NullPointerException( "path" );
}else if( path.equals( "" ) ){
throw new IllegalArgumentException( "path must not be empty." );
}else{
throw new NullPointerException( "date" );
}
}
/**
* ヘッダデータから 新しい LhaHeader の
* インスタンスを生成する。
* エンコードは LhaUtil.DefaultEncode が使用される。
*
* @param HeaderData ヘッダデータ
*
* @exception IndexOutOfBoundsException
* ヘッダデータが壊れているため
* データがあると仮定した位置が
* HeaderData の範囲外になった
* @exception IllegalArgumentException
* ヘッダレベルが 0,1,2,3 の何れでもないか、
* HeaderData が null の場合
*/
public LhaHeader( byte[] HeaderData ){
this();
if( HeaderData != null ){
try{
this.importHeader( HeaderData, LhaProperty.encoding );
}catch( UnsupportedEncodingException exception ){
throw new Error( "Java Runtime Environment not support " + LhaProperty.encoding + " encoding" );
}
}else{
throw new NullPointerException( "HeaderData" );
}
}
/**
* ヘッダデータから 新しい LhaHeader の
* インスタンスを生成する。
*
* @param HeaderData ヘッダデータ
* @param encode 文字列情報を解釈する際に使用する
* エンコード
*
* @exception IndexOutOfBoundsException
* ヘッダデータが壊れているため
* データがあると仮定した位置が
* HeaderData の範囲外になった
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
* @exception IllegalArgumentException
* ヘッダレベルが 0,1,2,3 の何れでもないか、
* HeaderData が null の場合
*/
public LhaHeader( byte[] HeaderData, String encode )
throws UnsupportedEncodingException {
this();
if( HeaderData != null && encode != null ){
this.importHeader( HeaderData, encode ); //throw UnsupportedEncodingException
}else if( HeaderData == null ){
throw new NullPointerException( "HeaderData" );
}else{
throw new NullPointerException( "encode" );
}
}
//------------------------------------------------------------------
// method of java.lang.Cloneable
//------------------------------------------------------------------
// public Object clone()
//------------------------------------------------------------------
/**
* このオブジェクトのコピーを作成して返す。
*
* @return このオブジェクトのコピー
*/
public Object clone(){
try{
return super.clone();
}catch( CloneNotSupportedException exception ){ //Ignore
throw new Error( "java.lang.Object is not support clone()." );
}
}
//------------------------------------------------------------------
// access method
//------------------------------------------------------------------
// getter
//------------------------------------------------------------------
// public String getCompressMethod()
// public long getOriginalSize()
// public long getCompressedSize()
// public Date getLastModified()
// public int getHeaderLevel()
// public String getPath()
// public int getCRC()
// public byte getOSID()
// protected byte[] getExtraData()
// protected byte getLevel0DosAttribute()
// private String getFileName()
// private String getDirName()
//------------------------------------------------------------------
/**
* データを圧縮した方法を識別する文字列を得る。
*
* @return 圧縮法文字列
*/
public String getCompressMethod(){
return this.Method;
}
/**
* データの圧縮前のサイズを得る。
*
* @return 圧縮前のサイズ
* LhaHeader( String path ) または
* LhaHeader( String path, Date date )で生成された
* インスタンスは初期状態ではサイズが不明のため
* LhaHeader.UNKNOWN( -1 ) を返す。
*
* @see LhaHeader#UNKNOWN
*/
public long getOriginalSize(){
return this.OriginalSize;
}
/**
* データの圧縮後のサイズを得る。
*
* @return 圧縮後のサイズ
* LhaHeader( String path ) または
* LhaHeader( String path, Date date )で生成された
* インスタンスは初期状態ではサイズが不明のため
* LhaHeader.UNKNOWN( -1 ) を返す。
*
* @see LhaHeader#UNKNOWN
*/
public long getCompressedSize(){
return this.CompressedSize;
}
/**
* データの最終更新日時を得る。
*
* @return データの最終更新日時
*/
public Date getLastModified(){
return new Date( this.LastModified.getTime() );
}
/**
* このヘッダのヘッダレベルを得る。
*
* @return ヘッダレベル
*/
public int getHeaderLevel(){
return this.HeaderLevel;
}
/**
* データの名前、
* もしくはデータがファイルであった場合のパス名を得る。
* パス名とはいっても、Windows 系の A: のような
* ドライブ名を含んではならない。
* パスデリミタには File.separator を使用する。
*
* @return データの名前、もしくは パス名。
*
* @see File#separator
*/
public String getPath(){
return this.Path;
}
/**
* データのCRC16値を得る。
*
* @return データのCRC16値
* LhaHeader( String path ) または
* LhaHeader( String path, Date date )で生成された
* インスタンスは初期状態ではCRCが不明のため
* LhaHeader.UNKNOWN( -1 ) を返す。
* レベル0ヘッダでCRC16値の
* フィールドが無い場合は
* LhaHeader.NO_CRC( -2 )を返す
*
* @see LhaHeader#UNKNOWN
* @see LhaHeader#NO_CRC
*/
public int getCRC(){
return this.CRC;
}
/**
* このヘッダを作成した OS の識別子を得る。
*
* @return OSの識別子
*/
public byte getOSID(){
return this.OSID;
}
/**
* レベル 0 ヘッダ、 レベル 1 ヘッダの時に
* 付加される可能性がある基本ヘッダ内の拡張データを得る。
*
* @return 拡張データ
*/
protected byte[] getExtraData(){
return (byte[])this.ExtraData.clone();
}
/**
* レベル 0 ヘッダに記される
* DOS のファイル属性を得る。
*
* @return DOS の ファイル属性
*/
protected byte getLevel0DosAttribute(){
return this.Level0DosAttribute;
}
/**
* パス名から切り分けられたファイル名を得る。
*
* @return ファイル名
*/
private String getFileName(){
return this.Path.substring(
this.Path.lastIndexOf( File.separatorChar ) + 1 );
}
/**
* パス名から切り分けられたディレクトリ名を得る。
*
* @return ディレクトリ名
*/
private String getDirName(){
return this.Path.substring( 0,
this.Path.lastIndexOf( File.separatorChar ) + 1 );
}
/**
* このLhaHeaderのデータを使用して ヘッダデータを生成し、
* それをバイト配列の形で得る。
* エンコードはデフォルトのものが使用される。
*
* @return バイト配列に格納したヘッダデータ
*
* @exception IllegalStateException
*
* - 圧縮法文字列をencodeでバイト配列に
* したものが 5byteで無い場合
*
- レベル0,1,2で ファイル名が長すぎるため
* ヘッダに収まりきらない。
*
- レベル1,2で共通拡張ヘッダが大きすぎて出力できない。
* そのためヘッダのCRC格納場所が無い。
*
- レベル0以外で CRC に レベル0ヘッダで
* CRC情報が無い事を示す特別な値である
* LhaHeader.NO_CRC( -2 ) が設定されていた。
*
- レベル0,1の時にLastModifiedがMS-DOS形式
* で表現できない範囲の時間であった場合
*
- レベル2,3の時にLastModifiedが4バイトの
* time_tで表現できない範囲の時間であった場合
*
- OriginalSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- OriginalSize が負値である場合
*
- レベル0,1,3 の時に OriginalSize が
* 4byte値で表現できない値である場合
*
- CompressedSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- CompressedSize が負値である場合
*
- レベル0,1,3 の時に CompressedSize が
* 4byte値で表現できない値である場合
*
- レベル2の時にOriginalSize または CompressedSizeが
* 4バイト値を超えるためファイルサイズヘッダが必要な際に
* 他の拡張ヘッダが大きすぎてファイルサイズヘッダが出力出来ない場合。
*
- CRC にCRC16値が不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- ヘッダレベルが 0,1,2,3 以外である場合
*
* の何れか。
*/
public byte[] getBytes(){
try{
return this.exportHeader( LhaProperty.encoding );
}catch( UnsupportedEncodingException exception ){
throw new Error( "Java Runtime Environment not support " + LhaProperty.encoding + " encoding" );
}
}
/**
* このLhaHeaderのデータを使用して ヘッダデータを生成し、
* それをバイト配列の形で得る。
*
* @param encode 文字列情報を出力する際に使用する
* エンコード
*
* @return バイト配列に格納したヘッダデータ
*
* @exception IllegalStateException
*
* - 圧縮法文字列をencodeでバイト配列に
* したものが 5byteで無い場合
*
- レベル0,1,2で ファイル名が長すぎるため
* ヘッダに収まりきらない。
*
- レベル1,2で共通拡張ヘッダが大きすぎて出力できない。
* そのためヘッダのCRC格納場所が無い。
*
- レベル0以外で CRC に レベル0ヘッダで
* CRC情報が無い事を示す特別な値である
* LhaHeader.NO_CRC( -2 ) が設定されていた。
*
- レベル0,1の時にLastModifiedがMS-DOS形式
* で表現できない範囲の時間であった場合
*
- レベル2,3の時にLastModifiedが4バイトの
* time_tで表現できない範囲の時間であった場合
*
- OriginalSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- OriginalSize が負値である場合
*
- レベル0,1,3 の時に OriginalSize が
* 4byte値で表現できない値である場合
*
- CompressedSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- CompressedSize が負値である場合
*
- レベル0,1,3 の時に CompressedSize が
* 4byte値で表現できない値である場合
*
- レベル2の時にOriginalSize または CompressedSizeが
* 4バイト値を超えるためファイルサイズヘッダが必要な際に
* 他の拡張ヘッダが大きすぎてファイルサイズヘッダが出力出来ない場合。
*
- CRC にCRC16値が不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- ヘッダレベルが 0,1,2,3 以外である場合
*
* の何れか。
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
public byte[] getBytes( String encode )
throws UnsupportedEncodingException {
return this.exportHeader( encode ); //throw UnsupportedEncodingException
}
//------------------------------------------------------------------
// access method
//------------------------------------------------------------------
// setter
//------------------------------------------------------------------
// public void setCompressMethod( String method )
// public void setOriginalSize( long size )
// public void setCompressedSize( long size )
// public void setLastModified( Date date )
// public void setHeaderLevel( int level )
// public void setPath( String path )
// public void setCRC( int crc )
// public void setOSID( byte id )
// protected void setExtraData( byte[] data )
// protected void setLevel0DosAttribute( byte attribute )
// private void setFileName( String filename )
// private void setDirName( String dirname )
//------------------------------------------------------------------
/**
* 圧縮法文字列を設定する。
*
* @param method 圧縮法文字列
*
* @exception IllegalArgumentException
* 圧縮法文字列が '-' で始まっていないか、
* '-' で終わっていない場合。
*/
public void setCompressMethod( String method ){
if( method == null ){
throw new NullPointerException( "method" );
}else if( !method.startsWith( "-" ) || !method.endsWith( "-" ) ){
throw new IllegalArgumentException( "method must starts with \'-\' and ends with \'-\'" );
}else{
this.Method = method;
}
}
/**
* 圧縮前データサイズを設定する。
* LhaHeader.UNKNOWN( -1 ) は サイズ不明を示す
* 特別な数字であるため設定できない。
* また レベル0,1,3 では処理できるのは 4バイト値のみであるため
* 4バイトで表現できない値を設定した場合 getByte() 時に例外を投げる。
*
* @param size 圧縮前データサイズ
*
* @exception IllegalArgumentException
* size に LhaHeader.UNKNOWN( -1 )を設定しようとした場合
*
* @see LhaHeader#UNKNOWN
*/
public void setOriginalSize( long size ){
if( size != LhaHeader.UNKNOWN ){
this.OriginalSize = size;
}else{
throw new IllegalArgumentException( "size must not LhaHeader.UNKNOWN( " + LhaHeader.UNKNOWN + " )" );
}
}
/**
* 圧縮後データサイズを設定する。
* LhaHeader.UNKNOWN( -1 ) は サイズ不明を示す
* 特別な数字であるため設定できない。
* また レベル0,1,3 では処理できるのは 4バイト値のみであるため
* 4バイトで表現できない値を設定した場合 getByte() 時に例外を投げる。
*
* @param size 圧縮後データサイズ
*
* @exception IllegalArgumentException
* size に LhaHeader.UNKNOWN を設定しようとした
*
* @see LhaHeader#UNKNOWN
*/
public void setCompressedSize( long size ){
if( size != LhaHeader.UNKNOWN ){
this.CompressedSize = size;
}else{
throw new IllegalArgumentException( "size must not LhaHeader.UNKNOWN( " + LhaHeader.UNKNOWN + " )" );
}
}
/**
* 圧縮データの最終更新日時を設定する。
* ヘッダレベルが 0,1 の場合は MsdosDateで表せる範囲内、
* ヘッダレベルが 2,3 の場合は 4byte の time_tで表せる範囲内
* の日付で無ければならない。
* 範囲内でなくても このメソッドは例外を投げないことに注意す
* ること。範囲内に無い場合は このメソッドは例外を投げないが、
* getBytes() 時に例外を投げる。
*
* @param date 最終更新日時
*
* @exception IllegalArgumentException
* date に null を設定しようとした場合
*/
public void setLastModified( Date date ){
if( date != null ){
this.LastModified = date;
}else{
throw new NullPointerException( "date" );
}
}
/**
* ヘッダレベルを設定する。
* 現在設定できるのは 0,1,2,3 のみとなっている。
* ヘッダレベルの変更はパスの最大長や、LastModified の制限範囲
* などを変化させるため注意が必要である。
*
* @param level ヘッダレベル
*/
public void setHeaderLevel( int level ){
this.HeaderLevel = level;
}
/**
* データの名前、もしくはデータがファイルである場合、
* データのパスを設定する。
* パスデリミタには File.separator を使用する。
* ヘッダレベルによって path にはバイト数の制限が存在するが、
* このメソッドは制限を越えた場合でも 例外を投げないことに
* 注意。制限を越えた場合は このメソッドは例外を投げないが、
* getBytes()時に例外を投げる
*
* @param path データの名前、もしくはファイル名
*
* @exception IllegalArgumentException
* path が空文字列である場合
*
* @see File#separator
*/
public void setPath( String path ){
if( path == null ){
throw new NullPointerException( "path" );
}else if( path.equals( "" ) ){
throw new IllegalArgumentException( "path must not empty." );
}else{
this.Path = path;
}
}
/**
* 圧縮前のデータの CRC16値を設定する。
* LhaHeader.UNKNOWN( -1 ) は サイズ不明を示す
* 特別な数字であるため設定できない。
* LhaHeader.NO_CRC( -2 ) は レベル0ヘッダの場
* 合に CRC値を出力しないことを意味する特別な値
* である。
* 他のヘッダレベルの時に LhaHeader.NO_CRC( -2 )
* を設定しても例外を投げないが getBytes() 時に
* 例外を投げるので注意すること。
* 有効なのは下位2バイトで、上位2バイトは無視される。
*
* @param crc データの圧縮前のCRC16値
*
* @exception IllegalArgumentException
* crc に LhaHeader.UNKNOWN を設定しようとした
*
* @see LhaHeader#UNKNOWN
* @see LhaHeader#NO_CRC
*/
public void setCRC( int crc ){
if( crc != LhaHeader.UNKNOWN ){
this.CRC = crc;
}else{
throw new IllegalArgumentException( "crc must not LhaHeader.UNKNOWN( " + LhaHeader.UNKNOWN + " )" );
}
}
/**
* このヘッダにOS固有の情報が含まれる場合、
* そのデータを解釈する手がかりとして OSの識別子を設定する。
*
* @param id OS識別子
*/
public void setOSID( byte id ){
this.OSID = id;
}
/**
* レベル 0,1ヘッダ時に使用される 基本ヘッダ内
* 拡張情報を設定する。
* 拡張情報のバイト数には制限が存在するが、このメソッドは
* 制限を越えても例外を投げないことに注意。制限を越えた場合
* getBytes()時に例外を投げる。
*
* @param data 拡張情報
* 拡張情報を出力しない場合は nullを設定する。
*/
protected void setExtraData( byte[] data ){
this.ExtraData = data;
}
/**
* レベル 0ヘッダの場合に出力される、
* MS-DOS のファイル属性を設定する。
*
* @param attribute MS-DOSのファイル属性
*/
protected void setLevel0DosAttribute( byte attribute ){
this.Level0DosAttribute = attribute;
}
/**
* filename で指定されるファイル名をパス名に設定する。
*
* @param filename ファイル名
*/
private void setFileName( String filename ){
this.Path = this.getDirName() + filename;
}
/**
* dirname で指定される ディレクトリ名をパス名に設定する。
*
* @param dirname ディレクトリ名
*/
private void setDirName( String dirname ){
this.Path = dirname + this.getFileName();
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// import base header
//------------------------------------------------------------------
// private void importLevel0Header( byte[] HeaderData, String encode )
// private void importLevel1Header( byte[] HeaderData, String encode )
// private void importLevel2Header( byte[] HeaderData, String encode )
// private void importLevel3Header( byte[] HeaderData, String encode )
// private void importHeader( byte[] HeaderData, String encode )
//------------------------------------------------------------------
/**
* HeaderDataをレベル0ヘッダのデータとして解釈し、
* このLhaHeaderに値を設定する。
*
* @param HeaderData ヘッダデータ
* @param encode 文字列情報を解釈する際に使用する
* エンコード
*
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private void importLevel0Header( byte[] HeaderData, String encode )
throws UnsupportedEncodingException {
//------------------------------------------------------------------
// ヘッダデータ位置の定義
final int HeaderSizeIndex = 0;
final int HeaderSize = ( HeaderData[ HeaderSizeIndex ] & 0xFF ) + 2;
final int CompressMethodIndex = 2;
final int CompressedSizeIndex = 7;
final int OriginalSizeIndex = 11;
final int LastModifiedIndex = 15;
final int DosAttributeIndex = 19;
final int HeaderLevelIndex = 20;
final int PathLengthIndex = 21;
final int PathLength = HeaderData[ PathLengthIndex ] & 0xFF;
final int PathIndex = 22;
final int CRCIndex = 22 + PathLength;
final int ExtraDataIndex = 24 + PathLength;
final int ExtraDataLength = HeaderSize - ExtraDataIndex;
//------------------------------------------------------------------
// ヘッダデータ読み込み
this.Method = new String( HeaderData, CompressMethodIndex, 5, encode );//After Java 1.1 throw UnsupportedEncodingException
this.CompressedSize = ((long)LittleEndian.readInt( HeaderData, CompressedSizeIndex )) & 0xFFFFFFFFL;
this.OriginalSize = ((long)LittleEndian.readInt( HeaderData, OriginalSizeIndex )) & 0xFFFFFFFFL;
this.LastModified = new MsdosDate( LittleEndian.readInt( HeaderData, LastModifiedIndex ) );
this.Level0DosAttribute = HeaderData[ DosAttributeIndex ];
this.HeaderLevel = HeaderData[ HeaderLevelIndex ] & 0xFF;
this.Path = new String( HeaderData, PathIndex, PathLength, encode ); //After Java 1.1 throw IndexOutOfBoundsException
this.Path = this.Path.replace( '\\', File.separatorChar );
if( CRCIndex + 2 <= HeaderSize ){
this.CRC = LittleEndian.readShort( HeaderData, CRCIndex ); //throw ArrayIndexOutOfBoundsException
if( 0 < ExtraDataLength ){
this.ExtraData = new byte[ExtraDataLength];
System.arraycopy( HeaderData, ExtraDataIndex,
this.ExtraData, 0, ExtraDataLength ); //throw IndexOutOfBoundsException
}
}else{
this.CRC = LhaHeader.NO_CRC;
}
}
/**
* HeaderDataをレベル1ヘッダのデータとして解釈し、
* このLhaHeaderに値を設定する。
*
* @param HeaderData ヘッダデータ
* @param encode 文字列情報を解釈する際に使用する
* エンコード
*
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private void importLevel1Header( byte[] HeaderData, String encode )
throws UnsupportedEncodingException {
//------------------------------------------------------------------
// 基本ヘッダ内データ位置の定義
final int BaseHeaderSizeIndex = 0;
final int BaseHeaderSize = (int)( HeaderData[BaseHeaderSizeIndex] & 0xFF ) + 2;
final int CompressMethodIndex = 2;
final int SkipSizeIndex = 7;
final int OriginalSizeIndex = 11;
final int LastModifiedIndex = 15;
final int HeaderLevelIndex = 20;
final int FileNameLengthIndex = 21;
final int FileNameLength = (int)( HeaderData[FileNameLengthIndex] & 0xFF );
final int FileNameIndex = 22;
final int CRCIndex = 22 + FileNameLength;
final int OSIDIndex = 24 + FileNameLength;
final int ExtraDataIndex = 25 + FileNameLength;
final int ExtraDataLength = BaseHeaderSize - ExtraDataIndex - 2;
//------------------------------------------------------------------
// 基本ヘッダデータ読み込み
this.Method = new String( HeaderData, CompressMethodIndex, 5, encode );//After Java 1.1 throws UnsupportedEncodingException
this.CompressedSize = ((long)LittleEndian.readInt( HeaderData, SkipSizeIndex )) & 0xFFFFFFFFL;
this.OriginalSize = ((long)LittleEndian.readInt( HeaderData, OriginalSizeIndex )) & 0xFFFFFFFFL;
this.LastModified = new MsdosDate( LittleEndian.readInt( HeaderData, LastModifiedIndex ) );
this.HeaderLevel = HeaderData[ HeaderLevelIndex ] & 0xFF;
this.Path = new String( HeaderData, FileNameIndex, FileNameLength, encode );//After Java 1.1 throw IndexOutOfBoundsException
this.CRC = LittleEndian.readShort( HeaderData, CRCIndex ); //throw ArrayIndexOutOfBoundsException
this.OSID = HeaderData[ OSIDIndex ]; //throw ArrayIndexOutOfBoundsException
if( 0 < ExtraDataLength ){
this.ExtraData = new byte[ExtraDataLength];
System.arraycopy( HeaderData, ExtraDataIndex,
this.ExtraData, 0, ExtraDataLength ); //throw IndexOutOfBoundsException
}
//------------------------------------------------------------------
// 拡張ヘッダデータの読み込み
boolean hasFileSize = false;
int index = BaseHeaderSize;
int length = LittleEndian.readShort( HeaderData, index - 2 ); //throw ArrayIndexOutOfBoundsException
while( length != 0 ){
if( !hasFileSize ){
this.CompressedSize -= length;
}
this.importExtHeader( HeaderData, index, length - 2, encode ); //throw IndexOutOfBoundsException
if( HeaderData[ index ] == (byte)0x42 ){
hasFileSize = true;
}
index += length;
length = LittleEndian.readShort( HeaderData, index - 2 ); //throw ArrayIndexOutOfBoundsException
}
}
/**
* HeaderDataをレベル2ヘッダのデータとして解釈し、
* このLhaHeaderに値を設定する。
*
* @param HeaderData ヘッダデータ
* @param encode 文字列情報を解釈する際に使用する
* エンコード
*
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private void importLevel2Header( byte[] HeaderData, String encode )
throws UnsupportedEncodingException {
//------------------------------------------------------------------
// 基本ヘッダ内データ位置の定義
final int HeaderSizeIndex = 0;
final int HeaderSize = LittleEndian.readShort( HeaderData, HeaderSizeIndex );
final int CompressMethodIndex = 2;
final int CompressedSizeIndex = 7;
final int OriginalSizeIndex = 11;
final int LastModifiedIndex = 15;
final int HeaderLevelIndex = 20;
final int CRCIndex = 21;
final int OSIDIndex = 23;
//------------------------------------------------------------------
// 基本ヘッダデータ読み込み
this.Method = new String( HeaderData, CompressMethodIndex, 5, encode );//After Java 1.1 throw UnsupportedEncodingException
this.CompressedSize = ((long)LittleEndian.readInt( HeaderData, CompressedSizeIndex )) & 0xFFFFFFFFL;
this.OriginalSize = ((long)LittleEndian.readInt( HeaderData, OriginalSizeIndex )) & 0xFFFFFFFFL;
this.LastModified = new Date( (long)LittleEndian.readInt( HeaderData, LastModifiedIndex ) * 1000L );
this.HeaderLevel = HeaderData[ HeaderLevelIndex ] & 0xFF;
this.CRC = LittleEndian.readShort( HeaderData, CRCIndex ); //throw ArrayIndexOutOfBoundsException
this.OSID = HeaderData[ OSIDIndex ]; //throw ArrayIndexOutOfBoundsException
//------------------------------------------------------------------
// 拡張ヘッダデータの読み込み
final int BaseHeaderSize = 26;
int index = BaseHeaderSize;
int length = LittleEndian.readShort( HeaderData, index - 2 ); //throw ArrayIndexOutOfBoundsException
while( length != 0 ){
this.importExtHeader( HeaderData, index, length - 2, encode ); //throw IndexOutOfBoundsException
index += length;
length = LittleEndian.readShort( HeaderData, index - 2 ); //throw ArrayIndexOutOfBoundsException
}
}
/**
* HeaderDataをレベル3ヘッダのデータとして解釈し、
* このLhaHeaderに値を設定する。
*
* @param HeaderData ヘッダデータ
* @param encode 文字列情報を解釈する際に使用する
* エンコード
*
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private void importLevel3Header( byte[] HeaderData, String encode )
throws UnsupportedEncodingException {
//------------------------------------------------------------------
// 基本ヘッダ内データ位置の定義
final int WordSizeIndex = 0;
final int WordSize = LittleEndian.readShort( HeaderData, WordSizeIndex );
final int CompressMethodIndex = 2;
final int CompressedSizeIndex = 7;
final int OriginalSizeIndex = 11;
final int LastModifiedIndex = 15;
final int HeaderLevelIndex = 20;
final int CRCIndex = 21;
final int OSIDIndex = 23;
//------------------------------------------------------------------
// 基本ヘッダデータ読み込み
this.Method = new String( HeaderData, CompressMethodIndex, 5, encode );//After Java 1.1 throw UnsupportedEncodingException
this.CompressedSize = ((long)LittleEndian.readInt( HeaderData, CompressedSizeIndex )) & 0xFFFFFFFFL;
this.OriginalSize = ((long)LittleEndian.readInt( HeaderData, OriginalSizeIndex )) & 0xFFFFFFFFL;
this.LastModified = new Date( (long)LittleEndian.readInt( HeaderData, LastModifiedIndex ) * 1000L );
this.HeaderLevel = HeaderData[ HeaderLevelIndex ] & 0xFF;
this.CRC = LittleEndian.readShort( HeaderData, CRCIndex ); //throw ArrayIndexOutOfBoundsException
this.OSID = HeaderData[ OSIDIndex ]; //throw ArrayIndexOutOfBoundsException
//------------------------------------------------------------------
// 拡張ヘッダデータの読み込み
final int BaseHeaderSize = 32;
int index = BaseHeaderSize;
int length = LittleEndian.readInt( HeaderData, index - 4 ); //throw ArrayIndexOutOfBoundsException
while( length != 0 ){
this.importExtHeader( HeaderData, index, length - 4, encode ); //throw IndexOutOfBoundsException
index += length;
length = LittleEndian.readInt( HeaderData, index - 4 ); //throw ArrayIndexOutOfBoundsException
}
}
/**
* HeaderData を LHAヘッダデータとして解釈し
* LhaHeader に値を設定する。
*
* @param HeaderData ヘッダデータ
* @param encode 文字列情報を解釈する際に使用する
* エンコード
*
* @exception IndexOutOfBoundsException
* ヘッダデータが壊れているため
* データがあると仮定した位置が
* HeaderData の範囲外になった
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
* @exception IllegalArgumentException
* ヘッダレベルが 0,1,2,3 の何れでもない。
*/
private void importHeader( byte[] HeaderData, String encode )
throws UnsupportedEncodingException {
final int HeaderLevelIndex = 20;
switch( HeaderData[HeaderLevelIndex] ){ //throws ArrayIndexOutOfBoundsException
case 0:
this.importLevel0Header( HeaderData, encode ); //After Java1.1 throws UnsupporetdEncodingException, InexOutOfBoundsException
break;
case 1:
this.importLevel1Header( HeaderData, encode ); //After Java1.1 throws UnsupporetdEncodingException, InexOutOfBoundsException
break;
case 2:
this.importLevel2Header( HeaderData, encode ); //After Java1.1 throws UnsupporetdEncodingException, InexOutOfBoundsException
break;
case 3:
this.importLevel3Header( HeaderData, encode ); //After Java1.1 throws UnsupporetdEncodingException, InexOutOfBoundsException
break;
default:
throw new IllegalArgumentException( "unknown header level \"" + HeaderData[ HeaderLevelIndex ] + "\"." );
}
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// import extend header
//------------------------------------------------------------------
// private void importCommonExtHeader( byte[] HeaderData, int index, int length )
// private void importFileNameExtHeader( byte[] HeaderData, int index,
// int length, String encode )
// private void importDirNameExtHeader( byte[] HeaderData, int index,
// int length, String encode )
// protected void importExtendHeader( byte[] HeaderData, int index,
// int length, String encode )
// private void importExtHeader( byte[] HeaderData, int index,
// int length, String encode )
//------------------------------------------------------------------
/**
* HeaderData から 共通拡張ヘッダを読み込む。
* このメソッドは共通拡張ヘッダに ヘッダ検査用のCRC16値以外
* のデータが存在した場合 共通拡張ヘッダを ExtraExtHeaders に
* 登録するだけである。
*
* @param HeaderData ヘッダデータ
* @param index HeaderData内の拡張ヘッダの開始位置
* @param length 拡張ヘッダの長さ
*/
private void importCommonExtHeader( byte[] HeaderData,
int index,
int length ){
//( 3 < length )の比較は 拡張ヘッダID(1byte)と
//ヘッダのCRC16値(2byte)以外にデータを含むかの判定。
//CRC16値以外の情報を持つなら その情報を保存するため
//ExtraExtHeadersに登録する。
if( 3 < length ){
if( this.ExtraExtHeaders == null ){
this.ExtraExtHeaders = new Vector();
}
byte[] ExtHeaderData = new byte[length];
System.arraycopy( HeaderData, index, ExtHeaderData, 0, length ); //throws IndexOutOfBoundsException
this.ExtraExtHeaders.addElement( ExtHeaderData );
}
}
/**
* HeaderData から ファイル名拡張ヘッダを読み込む。
*
* @param HeaderData ヘッダデータ
* @param index HeaderData内の拡張ヘッダの開始位置
* @param length 拡張ヘッダの長さ
* @param encode 文字列情報を解釈する際に使用する
* エンコード
*
* @exception UnsupportedEncodingException
* この例外が投げられることは無い。
*/
private void importFileNameExtHeader( byte[] HeaderData,
int index,
int length,
String encode )
throws UnsupportedEncodingException {
this.setFileName( new String( HeaderData, index + 1, length - 1, encode ) );//throws IndexOutOfBoundsException
}
/**
* HeaderData から ディレクトリ名拡張ヘッダを読み込む。
*
* @param HeaderData ヘッダデータ
* @param index HeaderData内の拡張ヘッダの開始位置
* @param length 拡張ヘッダの長さ
* @param encode 文字列情報を解釈する際に使用する
* エンコード
*
* @exception UnsupportedEncodingException
* この例外が投げられることは無い。
*/
private void importDirNameExtHeader( byte[] HeaderData,
int index,
int length,
String encode )
throws UnsupportedEncodingException {
final byte LhaFileSeparator = (byte)0xFF;
int off = 1;
String dir = "";
while( off < length ){
int len = 0;
while( off + len < length ){
if( HeaderData[ index + off + len ] != LhaFileSeparator ){
len++;
}else{
break;
}
}
if( off + len < length ){
dir += new String( HeaderData, index + off, len, encode ) + File.separator;
}else{
dir += new String( HeaderData, index + off, len, encode );
}
off += len + 1;
}
this.setDirName( dir );
}
/**
* HeaderData から ファイルサイズ拡張ヘッダを読み込む。
*
* @param HeaderData ヘッダデータ
* @param index HeaderData内の拡張ヘッダの開始位置
* @param length 拡張ヘッダの長さ
*/
private void importFileSizeHeader( byte[] HeaderData,
int index,
int length ){
if( length == 17 ){
this.CompressedSize = LittleEndian.readLong( HeaderData, index + 1 );
this.OriginalSize = LittleEndian.readLong( HeaderData, index + 9 );
}else{
}
}
/**
* 拡張ヘッダを読み込む。
* このメソッドをオーバーライドする事によって
* 様々な拡張ヘッダに対応することが可能となる。
* LhaHeader では 拡張ヘッダを private メンバである
* ExtraExtHeaders に登録するだけである。
*
* @param HeaderData ヘッダデータ
* @param index HeaderData内の拡張ヘッダの開始位置
* @param length 拡張ヘッダの長さ
* @param encode 文字列情報を解釈する際に使用する
* エンコード
*
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
protected void importExtendHeader( byte[] HeaderData,
int index,
int length,
String encode )
throws UnsupportedEncodingException {
if( this.ExtraExtHeaders == null ){
this.ExtraExtHeaders = new Vector();
}
byte[] ExtHeaderData = new byte[length];
System.arraycopy( HeaderData, index, ExtHeaderData, 0, length ); //throws IndexOutOfBoundsException
this.ExtraExtHeaders.addElement( ExtHeaderData );
}
/**
* HeaderData の index からはじまる length バイトの
* 拡張ヘッダを読み込む。
*
* @param HeaderData ヘッダデータ
* @param index HeaderData内の拡張ヘッダの開始位置
* @param length 拡張ヘッダの長さ
* @param encode 文字列情報を解釈する際に使用する
* エンコード
*
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private void importExtHeader( byte[] HeaderData,
int index,
int length,
String encode )
throws UnsupportedEncodingException {
final int ExtendHeaderIDIndex = 0;
switch( HeaderData[ index + ExtendHeaderIDIndex ] ){ //throws ArrayIndexOutOfBoundsException
case 0x00:
this.importCommonExtHeader( HeaderData, index, length ); //throws IndexOutOfBoundsException
break;
case 0x01:
this.importFileNameExtHeader( HeaderData, index, length, encode ); //throws IndexOutOfBoundsException
break;
case 0x02:
this.importDirNameExtHeader( HeaderData, index, length, encode ); //throws IndexOutOfBoundsException
break;
case 0x42:
this.importFileSizeHeader( HeaderData, index, length ); //throws IndexOutOfBoundsException
break;
default:
this.importExtendHeader( HeaderData, index, length, encode ); //throws UnsupportedEncodingException IndexOutOfBoundsException
}
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// export base header
//------------------------------------------------------------------
// private byte[] exportLevel0Header( String encode )
// private byte[] exportLevel1Header( String encode )
// private byte[] exportLevel2Header( String encode )
// private byte[] exportLevel3Header( String encode )
// private byte[] exportHeader( String encode )
//------------------------------------------------------------------
/**
* この LhaHeader の情報を使って
* レベル0ヘッダのデータを生成する。
* その際、ExtraData を含めるとヘッダサイズが
* 規定値に収まらない場合は ExtraData は含まれないことがある。
*
* @param encode 文字列情報を出力する際に使用する
* エンコード
*
* @exception IllegalStateException
*
* - 圧縮法文字列をencodeでバイト配列に
* したものが 5byteで無い場合
*
- Path が大きすぎるため レベル0ヘッダの
* 最大サイズに収まりきらない。
*
- LastModifiedがMS-DOS形式で
* 表現できない範囲の時間であった場合
*
- OriginalSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定
* されていた。
*
- OriginalSize が負値であるか、
* 4byte値で表現できない値である場合
*
- CompressedSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が
* 設定されていた。
*
- CompressedSize が負値であるか、
* 4byte値で表現できない値である場合
*
* の何れか
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private byte[] exportLevel0Header( String encode )
throws UnsupportedEncodingException {
//------------------------------------------------------------------
// ヘッダ出力準備
final int LHarcHeaderSize = 100;
final int CRCLength = ( this.CRC == LhaHeader.NO_CRC
|| this.CRC == LhaHeader.UNKNOWN ? 0 : 2 );
byte[] CompressMethod = this.Method.getBytes( encode ); //After Java 1.1 throw UnsupportedEncodingException
MsdosDate dosDate = null;
try{
dosDate = this.LastModified instanceof MsdosDate
? (MsdosDate)this.LastModified
: new MsdosDate( this.LastModified ); //throw IllegalArgumentException
}catch( IllegalArgumentException exception ){
throw new IllegalStateException( exception.toString() );
}
byte[] PathData = this.Path.replace( File.separatorChar,
'\\' ).getBytes( encode );//After Java 1.1
int HeaderLength = 22 + CRCLength + PathData.length;
byte[] ExtraData;
if( CRCLength != 0 && this.ExtraData != null
&& ( HeaderLength + this.ExtraData.length <= LHarcHeaderSize ) ){
ExtraData = this.ExtraData;
}else{
ExtraData = new byte[0];
}
HeaderLength += ExtraData.length;
//------------------------------------------------------------------
// ヘッダ正当性チェック
if( CompressMethod.length != 5 ){
throw new IllegalStateException( "CompressMethod doesn't follow Format." );
}
if( LHarcHeaderSize < HeaderLength ){
throw new IllegalStateException( "Header size too large." );
}
if( this.CompressedSize == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "CompressedSize must not be UNKNOWN." );
}
if( 0x0000000100000000L <= this.CompressedSize ){
throw new IllegalStateException( "CompressedSize must be 0xFFFFFFFF or less." );
}
if( this.CompressedSize < 0 ){
throw new IllegalStateException( "CompressedSize must be 0 or more." );
}
if( this.OriginalSize == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "OriginalSize must not be UNKNOWN." );
}
if( 0x0000000100000000L <= this.OriginalSize ){
throw new IllegalStateException( "OriginalSize must be 0xFFFFFFFF or less." );
}
if( this.OriginalSize < 0 ){
throw new IllegalStateException( "OriginalSize must be 0 or more." );
}
//------------------------------------------------------------------
// ヘッダ出力
byte[] HeaderData;
try{
ByteArrayOutputStream out = new ByteArrayOutputStream();
//出力するヘッダ長にはヘッダ先頭の ヘッダ長(1byte)、
//チェックサム(1byte)の2byteを含まないため -2 している。
out.write( HeaderLength - 2 );
out.write( 0 );
out.write( CompressMethod );
LittleEndian.writeInt( out, (int)this.CompressedSize );
LittleEndian.writeInt( out, (int)this.OriginalSize );
LittleEndian.writeInt( out, dosDate.getMsdosTime() );
out.write( this.Level0DosAttribute );
out.write( this.HeaderLevel );
out.write( PathData.length );
out.write( PathData );
if( this.CRC != -1 ){
LittleEndian.writeShort( out, this.CRC );
out.write( ExtraData );
}
out.close();
HeaderData = out.toByteArray();
}catch( IOException exception ){
throw new Error( "caught the IOException ( " + exception.getMessage() + " ) which should be never thrown by ByteArrayOutputStream." );
}
final int ChecksumIndex = 1;
HeaderData[ ChecksumIndex ] = (byte)LhaHeader.calcHeaderChecksum( HeaderData );
return HeaderData;
}
/**
* この LhaHeader の情報を使って
* レベル1ヘッダのデータを生成する。
* その際、ExtraData を含めるとヘッダサイズが
* 規定値に収まらない場合は ExtraData は含まれないことがある。
* また、拡張ヘッダで 65534バイト以上のサイズを
* 持つものは無視される。
*
* @param encode 文字列情報を出力する際に使用する
* エンコード
*
* @exception IllegalStateException
*
* - 圧縮法文字列をencodeでバイト配列に
* したものが 5byteで無い場合
*
- ファイル名が大きすぎるため
* 基本ヘッダにも拡張ヘッダにも収まりきらない。
*
- 共通拡張ヘッダが大きすぎて出力できない。
* そのためヘッダのCRC格納場所が無い。
*
- CRC に レベル0ヘッダで CRC情報が無い事を示す
* 特別な値である LhaHeader.NO_CRC( -2 ) が設定されていた。
*
- LastModifiedがMS-DOS形式で
* 表現できない範囲の時間であった場合
*
- OriginalSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- OriginalSize が負値であるか、
* 4byte値で表現できない値である場合
*
- CompressedSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- CompressedSize が負値であるか、
* 4byte値で表現できない値である場合
*
- CRC にCRC16値が不明である事を示す 特別な値
* である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
* の何れか
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private byte[] exportLevel1Header( String encode )
throws UnsupportedEncodingException {
//------------------------------------------------------------------
// ヘッダ出力準備
final int LHarcHeaderSize = 100;
boolean hasFileName = false; //ファイル名情報を持つかを示す
boolean hasCRC = false; //ヘッダのCRC情報を持つかを示す
byte[] CompressMethod = this.Method.getBytes( encode ); //After Java 1.1 throw UnsupportedEncodingException
MsdosDate dosDate;
try{
if( this.LastModified instanceof MsdosDate ){
dosDate = (MsdosDate)this.LastModified;
}else{
dosDate = new MsdosDate( this.LastModified ); //throw IllegalArgumentException
}
}catch( IllegalArgumentException exception ){
throw new IllegalStateException( exception.toString() );
}
int HeaderLength = 27;
byte[] ExtraData;
if( this.ExtraData != null && ( HeaderLength + this.ExtraData.length <= LHarcHeaderSize ) ){
ExtraData = this.ExtraData;
}else{
ExtraData = new byte[0];
}
HeaderLength += ExtraData.length;
byte[] FileName = this.getFileName().getBytes( encode ); //After Java 1.1
if( LHarcHeaderSize < HeaderLength + FileName.length ){
FileName = new byte[0];
}else{
hasFileName = true;
}
HeaderLength += FileName.length;
byte[][] ExtendHeaders = this.exportExtHeaders( encode );
long SkipSize = this.CompressedSize;
for( int i = 0 ; i < ExtendHeaders.length ; i++ ){
if( ExtendHeaders[i].length == 0
|| 65534 <= ExtendHeaders[i].length
|| ( ExtendHeaders[i][0] == 1 && hasFileName ) ){
ExtendHeaders[i] = null;
}else{
if( ExtendHeaders[i][0] == 0x00 ){
hasCRC = true;
}
if( ExtendHeaders[i][0] == 0x01 ){
hasFileName = true;
}
SkipSize += ExtendHeaders[i].length + 2;
}
}
//------------------------------------------------------------------
// ヘッダ正当性チェック
if( CompressMethod.length != 5 ){
throw new IllegalStateException( "CompressMethod doesn't follow Format." );
}
if( SkipSize != this.CompressedSize && !hasCRC ){
throw new IllegalStateException( "no Header CRC field." );
}
if( !hasFileName ){
throw new IllegalStateException( "no Filename infomation." );
}
if( this.CRC == LhaHeader.NO_CRC ){
throw new IllegalStateException( "no CRC value." );
}
if( this.CRC == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "CRC is UNKNOWN." );
}
if( this.CompressedSize == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "CompressedSize must not be UNKNOWN." );
}
if( 0x0000000100000000L <= this.CompressedSize ){
throw new IllegalStateException( "CompressedSize must be 0xFFFFFFFF or less." );
}
if( this.CompressedSize < 0 ){
throw new IllegalStateException( "CompressedSize must be 0 or more." );
}
if( this.OriginalSize == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "OriginalSize must not be UNKNOWN." );
}
if( 0x0000000100000000L <= this.OriginalSize ){
throw new IllegalStateException( "OriginalSize must be 0xFFFFFFFF or less." );
}
if( this.OriginalSize < 0 ){
throw new IllegalStateException( "OriginalSize must be 0 or more." );
}
if( 0x0000000100000000L <= SkipSize ){
throw new IllegalStateException( "SkipSize must be 0xFFFFFFFF or less." );
}
//------------------------------------------------------------------
// ヘッダ出力
byte[] HeaderData;
try{
ByteArrayOutputStream out = new ByteArrayOutputStream();
//出力するヘッダ長にはヘッダ先頭の ヘッダ長(1byte)、
//チェックサム(1byte)の2byteを含まないため -2 している。
out.write( HeaderLength - 2 );
out.write( 0 );
out.write( CompressMethod );
LittleEndian.writeInt( out, (int)SkipSize );
LittleEndian.writeInt( out, (int)this.OriginalSize );
LittleEndian.writeInt( out, dosDate.getMsdosTime() );
out.write( 0x20 );
out.write( this.HeaderLevel );
out.write( FileName.length );
out.write( FileName );
LittleEndian.writeShort( out, this.CRC );
out.write( this.OSID );
out.write( ExtraData );
for( int i = 0 ; i < ExtendHeaders.length ; i++ ){
if( ExtendHeaders[i] != null ){
LittleEndian.writeShort( out, ExtendHeaders[i].length + 2 );
out.write( ExtendHeaders[i] );
}
}
LittleEndian.writeShort( out, 0 );
out.close();
HeaderData = out.toByteArray();
}catch( IOException exception ){
throw new Error( "caught the IOException ( " + exception.getMessage() + " ) which should be never thrown by ByteArrayOutputStream." );
}
final int ChecksumIndex = 1;
final int CRCIndex = LhaHeader.getCRC16Position( HeaderData );
HeaderData[ChecksumIndex] = (byte)LhaHeader.calcHeaderChecksum( HeaderData );
if( hasCRC ){
LittleEndian.writeShort( HeaderData, CRCIndex,
LhaHeader.calcHeaderCRC16( HeaderData ) );
}
return HeaderData;
}
/**
* この LhaHeader の情報を使って
* レベル2ヘッダのデータを生成する。
* また、全拡張ヘッダが65536バイト以上のサイズになる場合は
* 共通拡張ヘッダ、ファイル名拡張ヘッダが最優先で格納される。
* 上記の 2つの拡張ヘッダのみで 65536バイト以上になる場合は
* 例外を投げる。その後は ディレクトリ名拡張ヘッダが優先され、
* その後は exportExtendHeaders(String) が出力した順に
* 優先して登録され、全ヘッダが 65536バイト以上に
* ならないように格納される。
*
* @param encode 文字列情報を出力する際に使用する
* エンコード
*
* @exception IllegalStateException
*
* - 圧縮法文字列をencodeでバイト配列に
* したものが 5byteで無い場合
*
- ファイル名が大きすぎるため
* 拡張ヘッダに収まりきらない。
*
- 共通拡張ヘッダが大きすぎて出力できない。
* そのためヘッダのCRC格納場所が無い。
*
- CRC に レベル0ヘッダで CRC情報が無い事を示す
* 特別な値である LhaHeader.NO_CRC( -2 ) が設定されていた。
*
- LastModifiedが4バイトのtime_tで
* 表現できない範囲の時間であった場合
*
- OriginalSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- OriginalSize が負値である場合
*
- CompressedSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- CompressedSize が負値である場合
*
- CRC にCRC16値が不明である事を示す 特別な値である
* LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- OriginalSize または CompressedSizeが4バイト値を
* 超えるためファイルサイズヘッダが必要な際に
* 他の拡張ヘッダが大きすぎて
* ファイルサイズヘッダが出力出来ない場合。
*
* の何れか。
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private byte[] exportLevel2Header( String encode )
throws UnsupportedEncodingException {
//------------------------------------------------------------------
// ヘッダ出力準備
final int MaxHeaderLength = 65535;
boolean hasFileName = false; //ファイル名情報を持つかを示す
boolean hasCRC = false; //ヘッダのCRC情報を持つかを示す
boolean needExtraByte = false; //ヘッダの先頭を0x00にしないために余分な1バイトを付加するかを示す。
boolean hasFileSize = false; //ファイルサイズヘッダを持つか示す。
byte[] CompressMethod = this.Method.getBytes( encode ); //After Java 1.1 throw UnsupportedEncodingException
int HeaderLength = 26;
boolean needFileSize = ( 0x0000000100000000L <= this.CompressedSize
|| 0x0000000100000000L <= this.OriginalSize );
byte[][] ExtendHeaders = this.exportExtHeaders( encode );
for( int i = 0 ; i < ExtendHeaders.length ; i++ ){
if( ExtendHeaders[i].length == 0
|| MaxHeaderLength <= HeaderLength + ExtendHeaders[i].length + 2 ){
ExtendHeaders[i] = null;
}else{
if( ExtendHeaders[i][0] == 0x00 ){
hasCRC = true;
}
if( ExtendHeaders[i][0] == 0x01 ){
hasFileName = true;
}
if( ExtendHeaders[i][0] == 0x42 ){
hasFileSize = true;
}
HeaderLength += ExtendHeaders[i].length + 2;
}
}
if( ( HeaderLength & 0xFF ) == 0 ){
HeaderLength++;
needExtraByte = true;
}
//------------------------------------------------------------------
// ヘッダ正当性チェック
if( CompressMethod.length != 5 ){
throw new IllegalStateException( "CompressMethod doesn't follow Format." );
}
if( this.LastModified.getTime() < 0
|| ( ( this.LastModified.getTime() / 1000L ) & 0xFFFFFFFF00000000L )
!= 0 ){
throw new IllegalStateException( "LastModified can not change to 4byte time_t format." );
}
if( !hasCRC ){
throw new IllegalStateException( "HeaderSize too large. can not contain CRC of the Header." );
}
if( !hasFileName ){
throw new IllegalStateException( "HeaderSize too large. can not contain Filename." );
}
if( needFileSize && !hasFileSize ){
throw new IllegalStateException( "HeaderSize too large. can not contain Filesize." );
}
if( this.CRC == LhaHeader.NO_CRC ){
throw new IllegalStateException( "no CRC." );
}
if( this.CRC == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "CRC must not be UNKNOWN." );
}
if( this.CompressedSize == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "CompressedSize must not be UNKNOWN." );
}
if( this.CompressedSize < 0 ){
throw new IllegalStateException( "CompressedSize must be 0 or more." );
}
if( this.OriginalSize == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "OriginalSize must not be UNKNOWN." );
}
if( this.OriginalSize < 0 ){
throw new IllegalStateException( "OriginalSize must be 0 or more." );
}
//------------------------------------------------------------------
// ヘッダ出力
byte[] HeaderData;
try{
ByteArrayOutputStream out = new ByteArrayOutputStream();
LittleEndian.writeShort( out, HeaderLength );
out.write( CompressMethod );
LittleEndian.writeInt( out, (int)this.CompressedSize );
LittleEndian.writeInt( out, (int)this.OriginalSize );
LittleEndian.writeInt( out,
(int)(this.LastModified.getTime() / 1000L) );
out.write( 0x20 );
out.write( this.HeaderLevel );
LittleEndian.writeShort( out, this.CRC );
out.write( this.OSID );
for( int i = 0 ; i < ExtendHeaders.length ; i++ ){
if( ExtendHeaders[i] != null ){
LittleEndian.writeShort( out, ExtendHeaders[i].length + 2 );
out.write( ExtendHeaders[i] );
}
}
LittleEndian.writeShort( out, 0 );
if( needExtraByte ) out.write( 0x00 );
out.close();
HeaderData = out.toByteArray();
}catch( IOException exception ){
throw new Error( "caught the IOException ( " + exception.getMessage() + " ) which should be never thrown by ByteArrayOutputStream." );
}
final int CRCIndex = LhaHeader.getCRC16Position( HeaderData );
LittleEndian.writeShort( HeaderData, CRCIndex,
LhaHeader.calcHeaderCRC16( HeaderData ) );
return HeaderData;
}
/**
* この LhaHeader の情報を使って
* レベル3ヘッダのデータを生成する。
*
* @param encode 文字列情報を出力する際に使用する
* エンコード
*
* @return バイト配列に格納したヘッダデータ
*
* @exception IllegalStateException
*
* - 圧縮法文字列をencodeでバイト配列に
* したものが 5byteで無い場合
*
- 共通拡張ヘッダが大きすぎて出力できない。
* そのためヘッダのCRC格納場所が無い。
*
- CRC に レベル0ヘッダで CRC情報が無い事を示す
* 特別な値である LhaHeader.NO_CRC( -2 ) が設定されていた。
*
- LastModifiedが4バイトのtime_tで
* 表現できない範囲の時間であった場合
* - OriginalSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
* - OriginalSize が負値であるか、
* 4byte値で表現できない値である場合
* - CompressedSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
* - CompressedSize が負値であるか、
* 4byte値で表現できない値である場合
* - CRC にCRC16値が不明である事を示す 特別な値である
* LhaHeader.UNKNOWN( -1 )が設定されていた。
*
* の何れか。
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private byte[] exportLevel3Header( String encode )
throws UnsupportedEncodingException {
//ヘッダ出力準備
final int WordSize = 4;
byte[] CompressMethod = this.Method.getBytes( encode ); //After Java 1.1 throw UnsupportedEncodingException
int HeaderLength = 32;
byte[][] ExtendHeaders = this.exportExtHeaders( encode );
for( int i = 0 ; i < ExtendHeaders.length ; i++ ){
if( ExtendHeaders[i].length == 0 ){
ExtendHeaders[i] = null;
}else{
HeaderLength += ExtendHeaders[i].length + 4;
}
}
//ヘッダ正当性チェック
if( CompressMethod.length != 5 ){
throw new IllegalStateException( "CompressMethod doesn't follow Format." );
}
if( this.LastModified.getTime() < 0
|| ( ( this.LastModified.getTime() / 1000L ) & 0xFFFFFFFF00000000L )
!= 0 ){
throw new IllegalStateException( "LastModified can not change to 4byte time_t format." );
}
if( this.CRC == LhaHeader.NO_CRC ){
throw new IllegalStateException( "no CRC value." );
}
if( this.CRC == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "CRC is UNKNOWN." );
}
if( this.CompressedSize == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "CompressedSize must not be UNKNOWN." );
}
if( 0x0000000100000000L <= this.CompressedSize ){
throw new IllegalStateException( "CompressedSize must be 0xFFFFFFFF or less." );
}
if( this.CompressedSize < 0 ){
throw new IllegalStateException( "CompressedSize must be 0 or more." );
}
if( this.OriginalSize == LhaHeader.UNKNOWN ){
throw new IllegalStateException( "OriginalSize must not be UNKNOWN." );
}
if( 0x0000000100000000L <= this.OriginalSize ){
throw new IllegalStateException( "OriginalSize must be 0xFFFFFFFF or less." );
}
if( this.OriginalSize < 0 ){
throw new IllegalStateException( "OriginalSize must be 0 or more." );
}
//ヘッダ出力
byte[] HeaderData;
try{
ByteArrayOutputStream out = new ByteArrayOutputStream();
LittleEndian.writeShort( out, WordSize );
out.write( CompressMethod );
LittleEndian.writeInt( out, (int)this.CompressedSize );
LittleEndian.writeInt( out, (int)this.OriginalSize );
LittleEndian.writeInt( out,
(int)(this.LastModified.getTime() / 1000L) );
out.write( 0x20 );
out.write( this.HeaderLevel );
LittleEndian.writeShort( out, this.CRC );
out.write( this.OSID );
LittleEndian.writeInt( out, HeaderLength );
for( int i = 0 ; i < ExtendHeaders.length ; i++ ){
if( ExtendHeaders[i] != null ){
LittleEndian.writeInt( out, ExtendHeaders[i].length + 4 );
out.write( ExtendHeaders[i] );
}
}
LittleEndian.writeInt( out, 0 );
out.close();
HeaderData = out.toByteArray();
}catch( IOException exception ){
throw new Error( "caught the IOException ( " + exception.getMessage() + " ) which should be never thrown by ByteArrayOutputStream." );
}
final int CRCIndex = LhaHeader.getCRC16Position( HeaderData );
LittleEndian.writeShort( HeaderData, CRCIndex,
LhaHeader.calcHeaderCRC16( HeaderData ) );
return HeaderData;
}
/**
* このLhaHeaderのデータを使用して ヘッダデータを生成し、
* それをバイト配列の形で得る。
*
* @param encode 文字列情報を出力する際に使用する
* エンコード
*
* @exception IllegalStateException
*
* - 圧縮法文字列をencodeでバイト配列に
* したものが 5byteで無い場合
*
- レベル0,1,2で ファイル名が長すぎるため
* ヘッダに収まりきらない。
*
- レベル1,2で共通拡張ヘッダが大きすぎて出力できない。
* そのためヘッダのCRC格納場所が無い。
*
- レベル0以外で CRC に レベル0ヘッダで
* CRC情報が無い事を示す特別な値である
* LhaHeader.NO_CRC( -2 ) が設定されていた。
*
- レベル0,1の時にLastModifiedがMS-DOS形式
* で表現できない範囲の時間であった場合
*
- レベル2,3の時にLastModifiedが4バイトの
* time_tで表現できない範囲の時間であった場合
*
- OriginalSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- OriginalSize が負値である場合
*
- レベル0,1,3 の時に OriginalSize が
* 4byte値で表現できない値である場合
*
- CompressedSize にサイズが不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- CompressedSize が負値である場合
*
- レベル0,1,3 の時に CompressedSize が
* 4byte値で表現できない値である場合
*
- レベル2の時にOriginalSize または CompressedSizeが
* 4バイト値を超えるためファイルサイズヘッダが必要な際に
* 他の拡張ヘッダが大きすぎてファイルサイズヘッダが出力出来ない場合。
*
- CRC にCRC16値が不明である事を示す
* 特別な値である LhaHeader.UNKNOWN( -1 )が設定されていた。
*
- ヘッダレベルが 0,1,2,3 以外である場合
*
* の何れか。
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private byte[] exportHeader( String encode )
throws UnsupportedEncodingException {
switch( this.HeaderLevel ){
case 0:
return this.exportLevel0Header( encode ); //throw UnsupportedEncodingException IllegalStateException
case 1:
return this.exportLevel1Header( encode ); //throw UnsupportedEncodingException IllegalStateException
case 2:
return this.exportLevel2Header( encode ); //throw UnsupportedEncodingException IllegalStateException
case 3:
return this.exportLevel3Header( encode ); //throw UnsupportedEncodingException IllegalStateException
default:
throw new IllegalStateException( "unknown header level \"" + this.HeaderLevel + "\"." );
}
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// export extend header
//------------------------------------------------------------------
// private byte[] exportCommonExtHeader()
// private byte[] exportFileNameExtHeader( String encode )
// private byte[] exportDirNameExtHeader( String encode )
// private byte[] exportFileSizeHeader()
// protected byte[][] exportExtendHeaders( String encode )
// private byte[][] exportExtHeader( String encode )
//------------------------------------------------------------------
/**
* 共通拡張ヘッダをバイト配列の形にして出力する。
* このメソッドは ExtraExtHeaders に 共通拡張ヘッダの情報が
* 登録されていればその情報を、でなれば 0で初期化された
* 3 バイトのバイト配列を返すだけである。
*
* @return 共通拡張ヘッダをバイト配列に格納したもの
*/
private byte[] exportCommonExtHeader(){
if( this.ExtraExtHeaders != null ){
for( int i = 0 ; i < this.ExtraExtHeaders.size() ; i++ ){
byte[] ExtendHeaderData = (byte[])this.ExtraExtHeaders.elementAt(i);
if( ExtendHeaderData[0] == 0x00 ){
return ExtendHeaderData;
}
}
}
return new byte[3];
}
/**
* ファイル名拡張ヘッダをバイト配列の形にして出力する。
* ファイル名拡張ヘッダは空でも出力される。
*
* @param encode 文字列情報を出力する際に使用する
* エンコード
*
* @return ファイル名拡張ヘッダをバイト配列に格納したもの
*/
private byte[] exportFileNameExtHeader( String encode )
throws UnsupportedEncodingException {
byte[] FileName = this.getFileName().getBytes( encode ); //After Java 1.1
byte[] ExtendHeaderData = new byte[ FileName.length + 1 ];
ExtendHeaderData[0] = 0x01; //拡張ヘッダIDを設定
System.arraycopy( FileName, 0, ExtendHeaderData, 1, FileName.length );
return ExtendHeaderData;
}
/**
* ディレクトリ名拡張ヘッダをバイト配列の形にして出力する。
* このメソッドでは ディレクトリ名拡張ヘッダは
* 空でも出力されるが、ディレクトリ名拡張ヘッダが空である
* 場合は exportExtHeaders() の段階で取り除かれる。
*
* @param encode 文字列情報を出力する際に使用する
* エンコード
*
* @return ディレクトリ名拡張ヘッダをバイト配列に格納したもの
*/
private byte[] exportDirNameExtHeader( String encode )
throws UnsupportedEncodingException {
final byte LhaFileSeparator = (byte)0xFF;
String dir = this.getDirName();
Vector vec = new Vector();
int index = 0;
int len = 0;
int length = 0;
while( index + len < dir.length() ){
if( dir.charAt( index + len ) == File.separatorChar ){
byte[] src = dir.substring( index, index + len ).getBytes( encode );
byte[] array = new byte[ src.length + 1 ];
System.arraycopy( src, 0, array, 0, src.length );
array[ src.length ] = LhaFileSeparator;
length += array.length;
vec.addElement( array );
index += len + 1;
len = 0;
}else if( index + len + 1 < dir.length() ){
byte[] array = dir.substring( index, index + len + 1 ).getBytes( encode );
length += array.length;
vec.addElement( array );
index += len + 1;
len = 0;
}else{
len++;
}
}
byte[] ExtendHeaderData = new byte[ length + 1 ];
ExtendHeaderData[0] = 0x02; //拡張ヘッダIDを設定
index = 1;
for( int i = 0 ; i < vec.size() ; i++ ){
byte[] array = (byte[])vec.elementAt( i );
System.arraycopy( array, 0, ExtendHeaderData, index, array.length );
index += array.length;
}
return ExtendHeaderData;
}
/**
* 64bitファイルサイズヘッダをバイト配列にして出力する。
* このメソッドはオリジナルサイズ、または圧縮後サイズが
* 32bit値で表現できる場合でもバイト配列を出力する。
* 必要の無い場合には exportExtHeaders() が出力を抑止する。
*
* @return 64bitファイルサイズヘッダ
*/
private byte[] exportFileSizeHeader(){
byte[] ExtendHeaderData = new byte[ 17 ];
ExtendHeaderData[0] = (byte)0x42;
LittleEndian.writeLong( ExtendHeaderData, 1, this.CompressedSize );
LittleEndian.writeLong( ExtendHeaderData, 9, this.OriginalSize );
return ExtendHeaderData;
}
/**
* 拡張ヘッダをバイト配列の形にして出力する。
* このメソッドをオーバーライドする事によって
* 様々な拡張ヘッダに対応することが可能となる。
* LhaHeader では private メンバである
* ExtraExtHeaders に登録された拡張ヘッダの情報を
* 返すだけである。
* 出力の形式は 第一バイト目に拡張ヘッダ識別子
* 続いて、拡張ヘッダデータが格納され、
* 次の拡張ヘッダの大きさは添付されない。
*
* @param encode 文字列情報を出力する際に使用する
* エンコード
*
* @return 1つの拡張ヘッダを1つのバイト配列に格納し、
* それを配列の形にしたもの
*
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
protected byte[][] exportExtendHeaders( String encode )
throws UnsupportedEncodingException {
if( this.ExtraExtHeaders != null ){
byte[][] ExtendHeaders = new byte[this.ExtraExtHeaders.size()][];
for( int i = 0 ; i < this.ExtraExtHeaders.size() ; i++ ){
ExtendHeaders[i] = (byte[])this.ExtraExtHeaders.elementAt(i);
}
return ExtendHeaders;
}else{
return new byte[0][];
}
}
/**
* 拡張ヘッダをバイト配列の形にして出力する。
*
* @param encode 文字列情報を出力する際に使用する
* エンコード
*
* @return 1つの拡張ヘッダを1つのバイト配列に格納し、
* それを全ての拡張ヘッダの配列の形にしたもの
*
* @exception UnsupportedEncodingException
* encode で指定されたエンコードが
* サポートされない場合
*/
private byte[][] exportExtHeaders( String encode )
throws UnsupportedEncodingException {
byte[] CommonExtHeader = this.exportCommonExtHeader();
byte[] FileNameExtHeader = this.exportFileNameExtHeader( encode );
byte[] DirNameExtHeader = this.exportDirNameExtHeader( encode );
byte[][] ExtraExtHeaders = this.exportExtendHeaders( encode );
Vector Headers = new Vector();
Headers.addElement( CommonExtHeader );
Headers.addElement( FileNameExtHeader );
if( 1 < DirNameExtHeader.length ){
Headers.addElement( DirNameExtHeader );
}
if( this.HeaderLevel == 2
&& ( 0x0000000100000000L <= this.CompressedSize
|| 0x0000000100000000L <= this.OriginalSize ) ){
Headers.addElement( this.exportFileSizeHeader() );
}
for( int i = 0 ; i < ExtraExtHeaders.length ; i++ ){
byte[] ExtendHeaderData = ExtraExtHeaders[i];
if( 0 < ExtendHeaderData.length
&& ExtendHeaderData[0] != 0x00
&& ExtendHeaderData[0] != 0x01
&& ExtendHeaderData[0] != 0x02 ){
Headers.addElement( ExtendHeaderData );
}
}
byte[][] ExtendHeaders = new byte[Headers.size()][];
for( int i = 0 ; i < ExtendHeaders.length ; i++ ){
ExtendHeaders[i] = (byte[])Headers.elementAt(i);
}
return ExtendHeaders;
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// public static boolean checkHeaderData( byte[] HeaderData )
//------------------------------------------------------------------
/**
* ヘッダデータが正当であるかをチェックする。
*
* @param HeaderData ヘッダデータをバイト配列に格納したもの
*
* @return ヘッダデータが正当であれば true
* 違えば false
*/
public static boolean checkHeaderData( byte[] HeaderData ){
final int HeaderLevelIndex = 20;
try{
switch( HeaderData[ HeaderLevelIndex ] & 0xFF ) {
case 0:
return LhaHeader.verifyHeaderChecksum( HeaderData );
case 1:
return LhaHeader.verifyHeaderChecksum( HeaderData )
&& ( LhaHeader.getCRC16Position( HeaderData ) == -1
|| LhaHeader.verifyHeaderCRC16( HeaderData ) );
case 2:
return LhaHeader.verifyHeaderCRC16( HeaderData );
case 3:
return LhaHeader.verifyHeaderCRC16( HeaderData );
}
}catch( ArrayIndexOutOfBoundsException exception ){ //Ignore
}
return false;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// check header
//------------------------------------------------------------------
// private static int getCRC16Position( byte[] HeaderData )
// private static int calcHeaderChecksum( byte[] HeaderData )
// private static int calcHeaderCRC16( byte[] HeaderData )
// private static int readHeaderChecksum( byte[] HeaderData )
// private static int readHeaderCRC16( byte[] HeaderData )
// private static boolean verifyHeaderCRC16( byte[] HeaderData )
// public static boolean checkHeaderData( byte[] HeaderData )
//------------------------------------------------------------------
/**
* ヘッダのCRC値を格納している位置を得る。
*
* @param HeaderData ヘッダデータをバイト配列に格納したもの
*
* @return ヘッダのCRC値の位置
* ヘッダがCRC値を持たない場合は -1
*/
private static int getCRC16Position( byte[] HeaderData ){
final int HeaderLevelIndex = 20;
int WordSize;
int position;
int length;
switch( HeaderData[ HeaderLevelIndex ] & 0xFF ){
case 1:
WordSize = 2;
position = length = ( HeaderData[ 0 ] & 0xFF ) + 2;
break;
case 2:
WordSize = 2;
position = length = 26;
break;
case 3:
WordSize = 4;
position = length = 32;
break;
default:
return -1;
}
while( true ){
if( 0 < length && position < HeaderData.length ){
length = 0;
for (int i = 0; i < WordSize ; i++){
length = ( length << 8 | ( HeaderData[ position - (1 + i) ] & 0xFF ) );
}
if( HeaderData[ position ] == 0 ){
return position + 1;
}
position += length;
} else {
return -1;
}
}
}
/**
* レベル0ヘッダ、レベル1ヘッダの
* ヘッダデータからチェックサム値を計算する。
*
* @param HeaderData ヘッダデータをバイト配列に格納したもの
*
* @return 計算されたヘッダのチェックサム値
*/
private static int calcHeaderChecksum( byte[] HeaderData ){
int length = HeaderData[ 0 ] & 0xFF;
LhaChecksum checksum = new LhaChecksum();
checksum.update( HeaderData, 2, length );
return (int)checksum.getValue();
}
/**
* レベル1ヘッダ、レベル2ヘッダ、レベル3ヘッダの
* ヘッダデータからCRC16値を計算する。
*
* @param HeaderData ヘッダデータをバイト配列に格納したもの
*
* @return 計算されたヘッダのCRC16値
*/
private static int calcHeaderCRC16( byte[] HeaderData ){
int position = LhaHeader.getCRC16Position( HeaderData );
int crcValue = 0;
if( position != -1 ){
crcValue = LittleEndian.readShort( HeaderData, position );
LittleEndian.writeShort( HeaderData, position, 0);
}
CRC16 crc16 = new CRC16();
crc16.update( HeaderData );
if( position != -1 ){
LittleEndian.writeShort( HeaderData, position, crcValue );
}
return (int)crc16.getValue();
}
/**
* レベル0ヘッダ、レベル1ヘッダの
* ヘッダデータからチェックサム値を読み込む。
*
* @param HeaderData ヘッダデータをバイト配列に格納したもの
*
* @return ヘッダに記録されたチェックサム値
*/
private static int readHeaderChecksum( byte[] HeaderData ) {
return HeaderData[ 1 ] & 0xFF;
}
/**
* レベル1ヘッダ、レベル2ヘッダ、レベル3ヘッダの
* ヘッダデータからCRC16値を読み込む。
*
* @param HeaderData ヘッダデータをバイト配列に格納したもの
*
* @return ヘッダに記録されたCRC16値
*/
private static int readHeaderCRC16( byte[] HeaderData ){
int position = LhaHeader.getCRC16Position( HeaderData );
if( position != -1 ){
return LittleEndian.readShort( HeaderData, position );
}else{
return -1;
}
}
/**
* チェックサム値によってヘッダデータの正当性をチェックする。
*
* @param HeaderData ヘッダデータをバイト配列に格納したもの
*
* @return チェックサム値によってヘッダデータの正当性が
* 証明されれば true、
* 証明されなければ false
*/
private static boolean verifyHeaderChecksum( byte[] HeaderData ){
final int HeaderLevelIndex = 20;
switch( HeaderData[ HeaderLevelIndex ] & 0xFF ) {
case 0:
case 1:
return LhaHeader.readHeaderChecksum( HeaderData )
== LhaHeader.calcHeaderChecksum( HeaderData );
default:
return false;
}
}
/**
* CRC16値によってヘッダデータの正当性をチェックする。
*
* @param HeaderData ヘッダデータをバイト配列に格納したもの
*
* @return CRC16値によってヘッダデータの正当性が
* 証明されれば true、
* 証明されなければ false
*/
private static boolean verifyHeaderCRC16( byte[] HeaderData ){
final int HeaderLevelIndex = 20;
switch( HeaderData[ HeaderLevelIndex ] & 0xFF ) {
case 1:
case 2:
case 3:
return LhaHeader.readHeaderCRC16( HeaderData )
== LhaHeader.calcHeaderCRC16( HeaderData );
default:
return false;
}
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// read header data from InputStream
//------------------------------------------------------------------
// public static byte[] getFirstHeaderData( InputStream in )
// public static byte[] getNextHeaderData( InputStream in )
//------------------------------------------------------------------
/**
* 入力ストリームから 最初のヘッダを読み込む。
* このメソッドはレベル1ヘッダ、もしくは レベル3ヘッダに
* 似たデータが存在すると、ヘッダ全てを読み込もうとして
* in.mark( 65536 ) の 限界を超えて 読み込む可能性があり、
* その結果 reset() できずに その間のデータを読み落とす
* 可能性がある。
* また、InputStream のmark/reset の実装次第では
* ストリーム終端付近で ヘッダに似たデータが存在すると
* ヘッダを全て読み込もうとして EndOfStreamに達してしまい、
* reset()できずに その間のデータを読み落とす可能性がある。
*
* @param in ヘッダデータを読み込む入力ストリーム
* ストリームは mark/resetのサポートを必要とする。
*
* @return 読み取られたヘッダデータ
* ヘッダが見つからずに EndOfStream に達した場合は null
*
* @exception IOException 入出力エラーが発生した場合
* @exception IllegalArgumentException
* in が mark/resetをサポートしない場合
*/
public static byte[] getFirstHeaderData( InputStream in )
throws IOException {
if( in.markSupported() ){
try {
int stock1 = -1;
int stock2 = -1;
int read;
while( 0 <= ( read = in.read() ) ) { //throw IOException
if( read == '-' && 0 < stock1 ){
in.mark( 65536 ); //65536で保証できるのはlevel0,2のみ
LhaHeader.ensureSkip( in, 3 ); //throw IOException
if( in.read() == '-' ){ //throw IOException
LhaHeader.ensureSkip( in, 13 ); //throw IOException
int HeaderLevel = in.read(); //throw IOException
in.reset(); //throw IOException
byte[] HeaderData;
switch( HeaderLevel ){
case 0:
HeaderData = LhaHeader.readLevel0HeaderData(
stock1, stock2, read, in ); //throw IOException
break;
case 1:
HeaderData = LhaHeader.readLevel1HeaderData(
stock1, stock2, read, in ); //throw IOException
break;
case 2:
HeaderData = LhaHeader.readLevel2HeaderData(
stock1, stock2, read, in ); //throw IOException
break;
case 3:
HeaderData = LhaHeader.readLevel3HeaderData(
stock1, stock2, read, in ); //throw IOException
break;
default:
HeaderData = null;
}
if( HeaderData != null
&& LhaHeader.checkHeaderData( HeaderData ) )
return HeaderData;
}
in.reset(); //throw IOException
}
stock1 = stock2;
stock2 = read;
}
}catch( EOFException exception ){ //Ignore
}
return null;
}else{
throw new IllegalArgumentException( "InputStream needed mark()/reset() support." );
}
}
/**
* 入力ストリームから 次のヘッダを読み込む。
* このメソッドはレベル1ヘッダ、もしくは レベル3ヘッダに
* 似たデータが存在すると、ヘッダ全てを読み込もうとして
* in.mark( 65536 ) の 限界を超えて 読み込む可能性があり、
* その結果 reset() できずに その間のデータを読み落とす
* 可能性がある。
* また、ストリーム終端付近で ヘッダに似たデータが存在する
* と ヘッダを全て読み込もうとして EndOfStreamに達してしまい、
* reset()できずに その間のデータを読み落とす可能性がある。
*
* @param in ヘッダデータを読み込む入力ストリーム
* ストリームは mark/resetのサポートを必要とする。
*
* @return 読み取られたヘッダデータ
* ヘッダが見つからずに EndOfStream に達した場合は null
*
* @exception IOException 入出力エラーが発生した場合
* @exception IllegalArgumentException
* in が mark/resetをサポートしない場合
*/
public static byte[] getNextHeaderData( InputStream in )
throws IOException {
if( in.markSupported() ){
try{
int first = in.read(); //throw IOException
if( 0 < first ){ // 負の値は EndOfStreamに到達、 0の場合は書庫終端に到達
int second = in.read(); //throw IOException
int third = in.read(); //throw IOException
in.mark( 65536 ); //65536で保証できるのはlevel0,2のみ
LhaHeader.ensureSkip( in, 3 ); //throw IOException
int seventh = in.read(); //throw IOException
if( third == '-' && seventh == '-' ){
LhaHeader.ensureSkip( in, 13 ); //throw IOException
int HeaderLevel = in.read(); //throw IOException
in.reset();
byte[] HeaderData;
switch( HeaderLevel ){
case 0:
HeaderData = LhaHeader.readLevel0HeaderData(
first, second, third, in );//throw IOException
break;
case 1:
HeaderData = LhaHeader.readLevel1HeaderData(
first, second, third, in );//throw IOException
break;
case 2:
HeaderData = LhaHeader.readLevel2HeaderData(
first, second, third, in );//throw IOException
break;
case 3:
HeaderData = LhaHeader.readLevel3HeaderData(
first, second, third, in );//throw IOException
break;
default:
HeaderData = null;
}
if( HeaderData != null && LhaHeader.checkHeaderData( HeaderData ) ){
return HeaderData;
}
}
}
}catch( EOFException exception ){ //Ignore
}
return null;
}else{
throw new IllegalArgumentException( "InputStream needed mark()/reset() support." );
}
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// read header data
//------------------------------------------------------------------
// private static byte[] readLevel0HeaderData( int HeaderLength,
// int HeaderChecksum, int CompressMethod1, InputStream in )
// private static byte[] readLevel1HeaderData( int BaseHeaderLength,
// int BaseHeaderChecksum, int CompressMethod1, InputStream in )
// private static byte[] readLevel2HeaderData( int HeaderLengthLow,
// int HeaderLengthHi, int CompressMethod1, InputStream in )
// private static byte[] readLevel2HeaderData( int WordSizeLow,
// int WordSizeHi, int CompressMethod1, InputStream in )
//------------------------------------------------------------------
/**
* 入力ストリームからレベル0ヘッダを読み込む
*
* @param HeaderLength ヘッダの長さ
* @param HeaderChecksum ヘッダのチェックサム
* @param CompressMethod1 圧縮法文字列
* @param in ヘッダデータを読み込む入力ストリーム
*
* @return 読み取られたヘッダデータ
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException ヘッダの読み込み途中で EndOfStreamに達した場合
*/
private static byte[] readLevel0HeaderData( int HeaderLength,
int HeaderChecksum,
int CompressMethod1,
InputStream in )
throws IOException {
byte[] HeaderData = new byte[ HeaderLength + 2 ];
HeaderData[0] = (byte)HeaderLength;
HeaderData[1] = (byte)HeaderChecksum;
HeaderData[2] = (byte)CompressMethod1;
int readed = 3;
int length = 0;
HeaderLength += 2;
while( readed < HeaderLength && 0 <= length ){
length = in.read( HeaderData, readed, HeaderLength - readed ); //throws IOException
readed += length;
}
if( readed == HeaderLength ){
return HeaderData;
}else{
throw new EOFException();
}
}
/**
* 入力ストリームからレベル1ヘッダを読み込む
*
* @param BaseHeaderLength 基本ヘッダの長さ
* @param BaseHeaderChecksum 基本ヘッダのチェックサム
* @param CompressMethod1 圧縮法文字列
* @param in ヘッダデータを読み込む入力ストリーム
*
* @return 読み取られたヘッダデータ。
* レベル1ヘッダでないことが判明した場合は nullを返す。
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException ヘッダの読み込み途中で EndOfStreamに達した場合
*/
private static byte[] readLevel1HeaderData( int BaseHeaderLength,
int BaseHeaderChecksum,
int CompressMethod1,
InputStream in )
throws IOException {
int HeaderLength = BaseHeaderLength + 2;
Vector headers = new Vector();
byte[] HeaderData = new byte[HeaderLength];
HeaderData[0] = (byte) BaseHeaderLength;
HeaderData[1] = (byte) BaseHeaderChecksum;
HeaderData[2] = (byte) CompressMethod1;
//ヘッダデータ取得
int readed = 0;
int length = 0;
do{
if( 0 == headers.size() ){
readed = 3;
}else{
readed = 0;
}
while( readed < HeaderLength && 0 <= length ){
length = in.read( HeaderData, readed, HeaderLength - readed ); //throws IOException
readed += length;
}
if( readed == HeaderLength ){
if( 0 == headers.size() && !LhaHeader.verifyHeaderChecksum( HeaderData ) ){
return null;
}else{
headers.addElement( HeaderData );
}
}else{
throw new EOFException();
}
length = HeaderLength;
HeaderLength = LittleEndian.readShort( HeaderData, HeaderLength - 2 );
HeaderData = new byte[ HeaderLength ];
}while( 0 < HeaderLength && readed == length );
//取得したヘッダデータを一つのバイト配列に
HeaderLength = 0;
for (int i = 0 ; i < headers.size(); i++ ){
HeaderLength += ((byte[])headers.elementAt(i)).length;
}
HeaderData = new byte[HeaderLength];
int position = 0;
for( int i = 0 ; i < headers.size() ; i++ ){
byte[] Data = (byte[])headers.elementAt(i);
System.arraycopy( Data, 0, HeaderData, position, Data.length );
position += Data.length;
}
return HeaderData;
}
/**
* 入力ストリームからレベル2ヘッダを読み込む
*
* @param HeaderLengthLow ヘッダの長さ下位バイト
* @param HeaderLengthHi ヘッダの長さ上位バイト
* @param CompressMethod1 圧縮法文字列
* @param in ヘッダデータを読み込む入力ストリーム
*
* @return 読み取られたヘッダデータ
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException ヘッダの読み込み途中で EndOfStreamに達した場合
*/
private static byte[] readLevel2HeaderData( int HeaderLengthLow,
int HeaderLengthHi,
int CompressMethod1,
InputStream in )
throws IOException {
int HeaderLength = ( HeaderLengthHi << 8 ) | HeaderLengthLow;
byte[] HeaderData = new byte[ HeaderLength ];
HeaderData[0] = (byte) HeaderLengthLow;
HeaderData[1] = (byte) HeaderLengthHi;
HeaderData[2] = (byte) CompressMethod1;
int readed = 3;
int length = 0;
while( readed < HeaderLength && 0 <= length ){
length = in.read( HeaderData, readed, HeaderLength - readed ); //throws IOException
readed += length;
}
if( readed == HeaderLength ){
return HeaderData;
}else{
throw new EOFException();
}
}
/**
* 入力ストリームからレベル3ヘッダを読み込む。
* このメソッドは 他の読み込みメソッドと違い、
* getNextHeaderData() において mark() されて
* いる事を前提としている。
*
* @param WordSizeLow ヘッダに使用されるワードサイズ 下位バイト
* @param WordSizeHi ヘッダに使用されるワードサイズ 上位バイト
* @param CompressMethod1 圧縮法文字列
* @param in ヘッダデータを読み込む入力ストリーム
*
* @return 読み取られたヘッダデータ。
* レベル3ヘッダでないことが判明した場合は nullを返す。
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException ヘッダの読み込み途中で EndOfStreamに達した場合
*/
private static byte[] readLevel3HeaderData( int WordSizeLow,
int WordSizeHi,
int CompressMethod1,
InputStream in )
throws IOException {
if( WordSizeLow == 0x04 && WordSizeHi == 0x00 ){
in.skip( 21 );
int HeaderLength = LittleEndian.readInt( in );
in.reset();
byte[] HeaderData = new byte[ HeaderLength ];
HeaderData[0] = (byte) WordSizeLow;
HeaderData[1] = (byte) WordSizeHi;
HeaderData[2] = (byte) CompressMethod1;
int readed = 3;
int length = 0;
while( readed < HeaderLength && 0 <= length ){
length = in.read( HeaderData, readed, HeaderLength - readed ); //throws IOException
readed += length;
}
if( readed == HeaderLength ){
return HeaderData;
}else{
throw new EOFException();
}
}else{
return null;
}
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// public static LhaHeader createInstance( byte[] HeaderData,
// String encoding, Properties property )
//------------------------------------------------------------------
/**
* property の キー"lha.header" に結び付けられた生成式を使用して
* HeaderData から LhaHeader のインスタンスを生成する。
*
* @param HeaderData ヘッダのデータを持つバイト配列
* @param property LhaProperty.parse() で LhaHeader のインスタンスが生成できるような
* 生成式を キー"lha.header" の値として持つプロパティ
*
* @return LhaHeader のインスタンス
*/
public static LhaHeader createInstance( byte[] HeaderData,
Properties property ){
String encoding = property.getProperty( "lha.encoding" );
if( encoding == null ){
encoding = LhaProperty.getProperty( "lha.encoding" );
}
String packages = property.getProperty( "lha.packages" );
if( packages == null ){
packages = LhaProperty.getProperty( "lha.packages" );
}
String generator = property.getProperty( "lha.header" );
if( generator == null ){
generator = LhaProperty.getProperty( "lha.header" );
}
Hashtable substitute = new Hashtable();
substitute.put( "data", HeaderData );
substitute.put( "encoding", encoding );
return (LhaHeader)LhaProperty.parse( generator, substitute, packages );
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// helper of InputStream
//------------------------------------------------------------------
// private static void ensureSkip( InputStream in, long len )
//------------------------------------------------------------------
/**
* InputStream を len バイトスキップする。
*
* @param in 入力ストリーム
* @param len スキップする長さ
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStream に達した場合。
*/
private static void ensureSkip( InputStream in, long len ) throws IOException {
while( 0 < len ){
long skiplen = in.skip( len );
if( skiplen <= 0 ){
if( 0 <= in.read() ){
len--;
}else{
throw new EOFException();
}
}else{
len -= skiplen;
}
}
}
}
//end of LhaHeader.java
jp/gr/java_conf/dangan/util/lha/LhaImmediateOutputStream.java 100644 0 0 101627 7575521002 21742 0 ustar 0 0 //start of LhaImmediateOutputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LhaImmediateOutputStream.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.lang.Math;
import java.util.Properties;
import jp.gr.java_conf.dangan.util.lha.CRC16;
import jp.gr.java_conf.dangan.util.lha.LhaHeader;
import jp.gr.java_conf.dangan.util.lha.LhaProperty;
import jp.gr.java_conf.dangan.util.lha.CompressMethod;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.lang.SecurityException;
import java.lang.IllegalStateException;
import java.lang.IllegalArgumentException;
import java.lang.NoSuchMethodError;
/**
* 接続されたRandomAccessFileに 圧縮データを出力するためのユーティリティクラス。
* java.util.zip.ZipOutputStream と似たインターフェイスを持つように作った。
* 圧縮失敗時( 圧縮後サイズが圧縮前サイズを上回った場合 )の処理を
* 手動で行わなければならない。
* 以下に そのようなコードを示す。
*
* LhaCompressFiles( String arcfile, File[] files ){
* LhaImmediateOutputStream lio = new LhaImmediateOutputStream( arcfile );
*
* for( int i = 0 ; i < files.length ; i++ ){
* RandomAccessFile raf = new RandomAccessFile( files[i] );
* LhaHeader header = new LhaHeader( files[i].getName() );
* header.setLastModified( new Date( files.lastModified() ) );
* header.setOriginalSize( files.length() );
* byte[] buffer = new byte[8192];
* int length;
*
* while( 0 <= ( length = raf.read( buffer ) ) ){
* lio.write( buffer, 0, length );
* }
*
* if( !lio.closeEntry() ){
* header.setCompressMethod( CompressMethod.LH0 );
* lio.putNextEntry( lhaheader );
* raf.seek( 0 );
* while( 0 <= ( length = raf.read( buffer ) ) ){
* lio.write( buffer, 0, length );
* }
* lio.closeEntry();
* }
*
* lio.close();
* }
*
* 進捗報告を実装する場合、このような処理をクラス内に隠蔽すると進捗報告は何秒間か
* 時によっては何十分も応答しなくなる。(例えばギガバイト級のデータを扱った場合)
* LhaRetainedOutputStream で発生する、このような事態を避けるために設計されている。
* また、JDK 1.1 以前では RandomAccessFile が setLength を持たないため、
* 書庫データの後ろに他のデータがある場合でもファイルサイズを切り詰めることが出来ない。
* この問題点は常にサイズ0の新しいファイルを開く事によって回避する事ができる。
*
*
* -- revision history --
* $Log: LhaImmediateOutputStream.java,v $
* Revision 1.2 2002/12/11 02:25:06 dangan
* [bug fix]
* jdk1.2 でコンパイルできなかった箇所を修正。
*
* Revision 1.1 2002/12/08 00:00:00 dangan
* [maintenance]
* LhaConstants から CompressMethod へのクラス名の変更に合わせて修正。
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [change]
* コンストラクタから 引数に String encode を取るものを廃止、
* Properties を引数に取るものを追加。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.2 $
*/
public class LhaImmediateOutputStream extends OutputStream{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private RandomAccessFile archive
//------------------------------------------------------------------
/**
* 書庫ファイル
*/
private RandomAccessFile archive;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// to compress a file
//------------------------------------------------------------------
// private OutputStream out
// private LhaHeader header
// private String encoding
// private long headerpos
// private CRC16 crc
//------------------------------------------------------------------
/**
* 圧縮用出力ストリーム
*/
private OutputStream out;
/**
* 現在圧縮中のヘッダ
*/
private LhaHeader header;
/**
* ヘッダの出力に使用したエンコーディング
*/
private String encoding;
/**
* ヘッダ位置
*/
private long headerpos;
/**
* CRC値算出用
*/
private CRC16 crc;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// property
//------------------------------------------------------------------
// private Properties property
//------------------------------------------------------------------
/**
* 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*/
private Properties property;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private LhaImmediateOutputStream()
// public LhaImmediateOutputStream( String filename )
// public LhaImmediateOutputStream( String filename, Properties property )
// public LhaImmediateOutputStream( File file )
// public LhaImmediateOutputStream( File file, Properties property )
// public LhaImmediateOutputStream( RandomAccessFile archive )
// public LhaImmediateOutputStream( RandomAccessFile archive, Properties property )
// private void constructerHelper( RandomAccesFile archive, Properties property )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ
* 使用不可
*/
private LhaImmediateOutputStream(){ }
/**
* filename のファイルに 圧縮データを出力するOutputStreamを構築する。
* 各圧縮形式に対応した符号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param filename 圧縮データを書きこむファイルの名前
*
* @exception FileNotFoundException
* filename で与えられたファイルが見つからない場合。
* @exception SecurityException
* セキュリティマネージャがファイルへのアクセスを許さない場合。
*
* @see LhaProperty#getProperties()
*/
public LhaImmediateOutputStream( String filename )
throws FileNotFoundException {
if( filename != null ){
RandomAccessFile file = new RandomAccessFile( filename, "rw" ); //throws FileNotFoundException, SecurityException
Properties property = LhaProperty.getProperties();
this.constructerHelper( file, property );
}else{
throw new NullPointerException( "filename" );
}
}
/**
* filename のファイルに 圧縮データを出力するOutputStreamを構築する。
*
* @param filename 圧縮データを書きこむファイルの名前
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*
* @exception FileNotFoundException
* filename で与えられたファイルが見つからない場合。
* @exception SecurityException
* セキュリティマネージャがファイルへのアクセスを許さない場合。
*
* @see LhaProperty
*/
public LhaImmediateOutputStream( String filename, Properties property )
throws FileNotFoundException {
if( filename != null ){
RandomAccessFile file = new RandomAccessFile( filename, "rw" ); //throws FileNotFoundException, SecurityException
this.constructerHelper( file, property );
}else{
throw new NullPointerException( "filename" );
}
}
/**
* filename のファイルに 圧縮データを出力するOutputStreamを構築する。
* 各圧縮形式に対応した符号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param filename 圧縮データを書きこむファイルの名前
*
* @exception FileNotFoundException
* filename で与えられたファイルが見つからない場合。
* @exception SecurityException
* セキュリティマネージャがファイルへのアクセスを許さない場合。
* @exception IOException
* JDK1.2 でコンパイルするためだけに存在する。
*
* @see LhaProperty#getProperties()
*/
public LhaImmediateOutputStream( File filename ) throws IOException {
if( filename != null ){
RandomAccessFile file = new RandomAccessFile( filename, "rw" ); //throws FileNotFoundException, SecurityException, IOException(jdk1.2)
Properties property = LhaProperty.getProperties();
this.constructerHelper( file, property );
}else{
throw new NullPointerException( "filename" );
}
}
/**
* filename のファイルに 圧縮データを出力するOutputStreamを構築する。
*
* @param filename 圧縮データを書きこむファイルの名前
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*
* @exception FileNotFoundException
* filename で与えられたファイルが見つからない場合。
* @exception SecurityException
* セキュリティマネージャがファイルへのアクセスを許さない場合。
* @exception IOException
* JDK1.2 でコンパイルするためだけに存在する。
*
* @see LhaProperty
*/
public LhaImmediateOutputStream( File filename, Properties property )
throws IOException {
if( filename != null ){
RandomAccessFile file = new RandomAccessFile( filename, "rw" ); //throws FileNotFoundException, SecurityException, IOException(jdk1.2)
this.constructerHelper( file, property );
}else{
throw new NullPointerException( "filename" );
}
}
/**
* fileに 圧縮データを出力するOutputStreamを構築する。
* 各圧縮形式に対応した符号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param file RandomAccessFile のインスタンス。
*
* - 既に close() されていない事。
*
- コンストラクタの mode には "rw" オプションを使用して、
* 読みこみと書きこみが出来るように生成されたインスタンスであること。
*
* の条件を満たすもの。
*
* @see LhaProperty#getProperties()
*/
public LhaImmediateOutputStream( RandomAccessFile file ){
if( file != null ){
Properties property = LhaProperty.getProperties();
this.constructerHelper( file, property );
}else{
throw new NullPointerException( "out" );
}
}
/**
* fileに 圧縮データを出力するOutputStreamを構築する。
* 各圧縮形式に対応した符号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param file RandomAccessFile のインスタンス。
*
* - 既に close() されていない事。
*
- コンストラクタの mode には "rw" オプションを使用して、
* 読みこみと書きこみが出来るように生成されたインスタンスであること。
*
* の条件を満たすもの。
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*
* @see LhaProperty
*/
public LhaImmediateOutputStream( RandomAccessFile file,
Properties property ){
if( file != null
&& property != null ){
this.constructerHelper( file, property ); //throws UnsupportedEncodingException
}else if( file == null ){
throw new NullPointerException( "null" );
}else{
throw new NullPointerException( "property" );
}
}
/**
* コンストラクタの初期化処理を担当するメソッド。
*
* @param file RandomAccessFile のインスタンス。
*
* - 既に close() されていない事。
*
- コンストラクタの mode には "rw" オプションを使用して、
* 読みこみと書きこみが出来るように生成されたインスタンスであること。
*
* の条件を満たすもの。
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*/
private void constructerHelper( RandomAccessFile file,
Properties property ){
this.archive = file;
this.out = null;
this.header = null;
this.headerpos = -1;
this.crc = new CRC16();
this.property = property;
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* 現在のエントリに1バイトのデータを書きこむ。
*
* @param data 書きこむデータ
*
* @exception IOException 入出力エラーが発生した場合。
*/
public void write( int data ) throws IOException {
if( this.out != null ){
if( this.header != null ){
crc.update( data );
}
this.out.write( data );
}else{
throw new IOException( "no entry" );
}
}
/**
* 現在のエントリに bufferの内容を全て書き出す。
*
* @param buffer 書き出すデータの入ったバイト配列
*
* @exception IOException 入出力エラーが発生した場合。
*/
public void write( byte[] buffer ) throws IOException {
this.write( buffer, 0, buffer.length );
}
/**
* 現在のエントリに bufferの indexから
* lengthバイトのデータを書き出す。
*
* @param buffer 書き出すデータの入ったバイト配列
* @param index buffer内の書き出すべきデータの開始位置
* @param length データのバイト数
*
* @exception IOException 入出力エラーが発生した場合。
*/
public void write( byte[] buffer, int index, int length ) throws IOException {
if( this.out != null ){
if( this.header != null ){
crc.update( buffer, index, length );
}
this.out.write( buffer, index, length );
}else{
throw new IOException( "no entry" );
}
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* 現在書き込み中のエントリのデータを強制的に出力先に書き出す。
* これは PostLzssEncoder, LzssOutputStream の規約どおり
* flush() しなかった場合とは別のデータを出力する。
* (大抵の場合は 単に圧縮率が低下するだけである。)
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PostLzssEncoder#flush()
* @see LzssOutputStream#flush()
*/
public void flush() throws IOException {
if( this.out != null ){
this.out.flush(); //throws IOException
}else{
throw new IOException( "no entry" );
}
}
/**
* 出力先に全てのデータを出力し、ストリームを閉じる。
* また、使用していた全てのリソースを解放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
if( this.out != null ){
this.closeEntry(); //throws IOException
}
//ターミネータを出力
this.archive.write( 0 ); //throws IOException
try{
this.archive.setLength( this.archive.getFilePointer() ); //After Java1.2 throws IOException
}catch( NoSuchMethodError error ){
}
this.archive.close(); //throws IOException
this.archive = null;
this.crc = null;
this.property = null;
this.encoding = null;
}
//------------------------------------------------------------------
// original method ( on the model of java.util.zip.ZipOutputStream )
//------------------------------------------------------------------
// manipulate entry
//------------------------------------------------------------------
// public void putNextEntry( LhaHeader header )
// public void putNextEntryAlreadyCompressed( LhaHeader header )
// public void putNextEntryNotYetCompressed( LhaHeader header )
// public void closeEntry()
//------------------------------------------------------------------
/**
* 新しいエントリを書き込むようにストリームを設定する。
* このメソッドは 既に圧縮済みのエントリの場合は
* putNextEntryAlreadyCompressed(),
* 未だに圧縮されていない場合は
* putNextEntryNotYetCompressed() を呼び出す。
* 圧縮されているかの判定は、
*
* - header.getCompressedSize()
* - header.getCRC()
*
* のどれか一つでも LhaHeader.UNKNOWN であれば未だに圧縮されていないとする。
* header には正確な OriginalSize が指定されている必要がある。
*
* @param header 書きこむエントリについての情報を持つ
* LhaHeaderのインスタンス。
*
* @exception IOException 入出力エラーが発生した場合
* @exception IllegalArgumentException
* header.getOriginalSize() が LhaHeader.UNKNOWN を返す場合
*/
public void putNextEntry( LhaHeader header ) throws IOException {
if( header.getCompressedSize() == LhaHeader.UNKNOWN
|| header.getCRC() == LhaHeader.UNKNOWN ){
this.putNextEntryNotYetCompressed( header ); //throws IOException
}else{
this.putNextEntryAlreadyCompressed( header ); //throws IOException
}
}
/**
* 既に圧縮済みのエントリを書きこむようにストリームを設定する。
* 圧縮済みデータが正しい事は、呼び出し側が保証する事。
*
* @param header 書きこむエントリについての情報を持つ
* LhaHeaderのインスタンス。
*
* @exception IOException 入出力エラーが発生した場合
* @exception IllegalArgumentException
*
* - header.getOriginalSize() が LhaHeader.UNKNOWN を返す場合
*
- header.getComressedSize() が LhaHeader.UNKNOWN を返す場合
*
- header.getCRC() が LhaHeader.UNKNOWN を返す場合
*
* の何れか。
* @exception IllegalStateException
* 以前のエントリが未だに closeEntry() されていない場合
*/
public void putNextEntryAlreadyCompressed( LhaHeader header )
throws IOException {
if( this.out == null ){
if( header.getOriginalSize() != LhaHeader.UNKNOWN
&& header.getCompressedSize() != LhaHeader.UNKNOWN
&& header.getCRC() != LhaHeader.UNKNOWN ){
this.headerpos = this.archive.getFilePointer();
this.encoding = this.property.getProperty( "lha.encoding" );
if( this.encoding == null ){
this.encoding = LhaProperty.getProperty( "lha.encoding" );
}
this.archive.write( header.getBytes( encoding ) ); //throws IOException
this.out = new RandomAccessFileOutputStream( this.archive, header.getCompressedSize() );
}else if( header.getOriginalSize() == LhaHeader.UNKNOWN ){
throw new IllegalArgumentException( "OriginalSize must not \"LhaHeader.UNKNOWN\"." );
}else if( header.getCompressedSize() == LhaHeader.UNKNOWN ){
throw new IllegalArgumentException( "CompressedSize must not \"LhaHeader.UNKNOWN\"." );
}else{
throw new IllegalArgumentException( "CRC must not \"LhaHeader.UNKNOWN\"." );
}
}else{
throw new IllegalStateException( "entry is not closed." );
}
}
/**
* 未だに圧縮されていないエントリを書きこむようにストリームを
* 設定する。header に CompressedSize,CRCが指定されていても無
* 視される。このメソッドに渡される header には
* LhaHeader.setOriginalSize() を用いて 正確なオリジナルサイズ
* が指定されている必要がある。
*
* @param header 書きこむエントリについての情報を持つ
* LhaHeaderのインスタンス。
*
* @exception IOException 入出力エラーが発生した場合
* @exception IllegalArgumentException
* header.getOriginalSize() が
* LhaHeader.UNKNOWN を返した場合
* @exception IllegalStateException
* 以前のエントリが未だに
* closeEntry() されていない場合
*/
public void putNextEntryNotYetCompressed( LhaHeader header )
throws IOException {
if( out == null ){
if( header.getOriginalSize() != LhaHeader.UNKNOWN ){
this.crc.reset();
this.headerpos = this.archive.getFilePointer();
this.header = (LhaHeader)header.clone();
this.header.setCompressedSize( 0 );
this.header.setCRC( 0 );
this.encoding = this.property.getProperty( "lha.encoding" );
if( this.encoding == null ){
this.encoding = LhaProperty.getProperty( "lha.encoding" );
}
this.archive.write( this.header.getBytes( encoding ) );
this.out = new RandomAccessFileOutputStream( this.archive, header.getOriginalSize() );
this.out = CompressMethod.connectEncoder( this.out,
header.getCompressMethod(),
this.property );
}else{
throw new IllegalArgumentException( "OriginalSize must not \"LhaHeader.UNKNOWN\"." );
}
}else{
throw new IllegalStateException( "entry is not closed." );
}
}
/**
* 現在出力中のエントリを閉じ、次のエントリが出力可能な状態にする。
* putNextEntryNotYetCompressed() で開いたエントリを閉じる場合
* このメソッドは圧縮に失敗した(圧縮後サイズが圧縮前サイズを上回った)場合、
* エントリ全体を書き込み先 の RandomAccessFile から削除する。
* この削除処理は単に ファイルポインタを エントリ開始位置まで巻き戻すだけなので
* RandomAccessFile に setLength() が無い jdk1.1 以前では
* エントリを無圧縮(もしくは他の圧縮法)で再出力しない場合、
* 書庫データの終端以降に圧縮に失敗した不完全なデータが残ったままになる。
*
* @return エントリが出力された場合は true、
* 圧縮前よりも圧縮後の方がサイズが大きくなったため、
* エントリが削除された場合は false。
* また、現在処理中のエントリが無かった場合も true を返す。
*
* @exception IOException 入出力エラーが発生した場合
*/
public boolean closeEntry() throws IOException {
if( this.out != null ){
this.out.close();
if( this.header != null ){
long pos = this.archive.getFilePointer();
long size = ( pos - this.headerpos
- this.header.getBytes( this.encoding ).length );
this.header.setCompressedSize( size );
if( this.header.getCRC() != LhaHeader.NO_CRC ){
this.header.setCRC( (int)this.crc.getValue() );
}
this.archive.seek( this.headerpos );
if( this.header.getCompressMethod().equals( CompressMethod.LH0 )
|| this.header.getCompressMethod().equals( CompressMethod.LHD )
|| this.header.getCompressMethod().equals( CompressMethod.LZ4 )
|| this.header.getCompressedSize() < this.header.getOriginalSize() ){
this.archive.write( this.header.getBytes( this.encoding ) );
this.archive.seek( pos );
this.header = null;
this.out = null;
return true;
}else{
this.header = null;
this.out = null;
return false;
}
}else{
this.out = null;
return true;
}
}else{
return true;
}
}
//------------------------------------------------------------------
// inner classes
//------------------------------------------------------------------
// private static class RandomAccessFileOutputStream
//------------------------------------------------------------------
/**
* RandomAccessFileをOutputStreamのインタフェイスに合わせるためのラッパクラス
*/
private static class RandomAccessFileOutputStream extends OutputStream {
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private RandomAccessFile archive
//------------------------------------------------------------------
/**
* 出力先RandomAccessFile
*/
private RandomAccessFile archive;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// position
//------------------------------------------------------------------
// private long pos
// private long limit
//------------------------------------------------------------------
/**
* 現在処理位置
*/
private long pos;
/**
* 格納限界
*/
private long limit;
//------------------------------------------------------------------
// consutructor
//------------------------------------------------------------------
// public RandomAccessFileOutputStream( RandomAccessFile archive,
// long length )
//------------------------------------------------------------------
/**
* RandomAccessFile をラップした OutputStream を構築する。
*
* @param archive 出力先のRandomAccessFile
* @param length 出力限界長
*
* @exception IOException 入出力エラーエラーが発生した場合
*/
public RandomAccessFileOutputStream( RandomAccessFile archive,
long length ) throws IOException {
this.archive = archive;
this.pos = this.archive.getFilePointer(); //throws IOException
this.limit = this.pos + length;
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* 接続されたRandomAccessFileに1バイト書きこむ。
* コンストラクタに渡された限界を超えて書き込もうとした場合は
* 何も行わない。
*
* @param data 書きこむ1byteのデータ
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( int data ) throws IOException {
if( this.pos < this.limit ){
this.pos++;
this.archive.write( data ); //throws IOException
}
}
/**
* 接続されたRandomAccessFileにbufferの内容を全て書きこむ。
* コンストラクタに渡された限界を超えて書き込もうとした場合は
* 何も行わない。
*
* @param buffer 書きこむデータの入ったバイト配列
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer ) throws IOException {
this.write( buffer, 0, buffer.length ); //throws IOException
}
/**
* 接続されたRandomAccessFileにbufferの内容をindexから lengthバイト書きこむ。
* コンストラクタに渡された限界を超えて書き込もうとした場合は
* 何も行わない。
*
* @param buffer 書きこむデータの入ったバイト配列
* @param index buffer内の書きこむデータの開始位置
* @param length 書きこむデータ量
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer, int index, int length )
throws IOException {
if( this.limit < this.pos + length ){
length = (int)Math.max( this.limit - this.pos, 0 );
}
this.archive.write( buffer, index, length ); //throws IOException
this.pos += length;
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// public void close()
//------------------------------------------------------------------
/**
* このストリームを閉じて 使用していたリソースを開放する。
*/
public void close(){
this.archive = null;
}
}
}
//end of LhaImmediateOutputStream.java
jp/gr/java_conf/dangan/util/lha/LhaInputStream.java 100644 0 0 51536 7706513627 17717 0 ustar 0 0 //start of LhaInputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LhaInputStream.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.util.Properties;
import jp.gr.java_conf.dangan.io.LimitedInputStream;
import jp.gr.java_conf.dangan.io.DisconnectableInputStream;
import jp.gr.java_conf.dangan.util.lha.LhaHeader;
import jp.gr.java_conf.dangan.util.lha.LhaProperty;
import jp.gr.java_conf.dangan.util.lha.CompressMethod;
//import exceptions
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.NullPointerException;
import java.lang.Error;
/**
* 接続されたストリームからLHA書庫データを読みこみ、
* エントリを解凍しつつ読み込むためのユーティリティクラス。
* java.util.zip.ZipInputStream と似たインターフェイスを持つように作った。
* 壊れた書庫の処理に関しては壊れたエントリ以降の
* 壊れていないエントリも正常に読みこめない可能性がある。
*
*
* -- revision history --
* $Log: LhaInputStream.java,v $
* Revision 1.1.2.1 2003/07/20 13:22:31 dangan
* [bug fix]
* getNextEntry() で CompressMethod.connectDecoder に
* this.limit を渡すべきところで this.in を渡していた。
*
* Revision 1.1 2002/12/08 00:00:00 dangan
* [maintenance]
* LhaConstants から CompressMethod へのクラス名の変更に合わせて修正。
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [change]
* コンストラクタから 引数に String encode を取るものを廃止、
* Properties を引数に取るものを追加。
* 書庫終端に達した場合はそれ以上読み込めないように修正。
* available() の振る舞いを java.util.zip.ZipInputStream と同じように
* エントリの終端に達していない場合は 1 エントリの終端に達した場合は 0 を返すように変更。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1.2.1 $
*/
public class LhaInputStream extends InputStream{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private InputStream source
// private boolean alreadyOpenedFirstEnrty
// private boolean reachedEndOfArchive
//------------------------------------------------------------------
/**
* LHA書庫形式のデータを供給するInputStream。
*/
private InputStream source;
/**
* 既に最初のエントリを読み込んでいるかを示す。
*/
private boolean alreadyOpenedFirstEnrty;
/**
* 書庫終端に達したかを示す。
*/
private boolean reachedEndOfArchive;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// for taking out a file from the archive
//------------------------------------------------------------------
// private InputStream in
// private LimitedInputStream limit
// private boolean reachedEndOfEntry
//------------------------------------------------------------------
/**
* LHA書庫内の1エントリの解凍されたデータ
* を供給する InputStream。
*/
private InputStream in;
/**
* LHA書庫内の1エントリの圧縮されたデータ
* を供給するLimitedInputStream。
* closeEntry 時にスキップするため。
*/
private LimitedInputStream limit;
/**
* 現在処理中のエントリの終端に達した時に true にセットされる。
*/
private boolean reachedEndOfEntry;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// backup for mark/reset
//------------------------------------------------------------------
// private boolean markReachedEndOfEntry
//------------------------------------------------------------------
/** reachEndOfEntry のバックアップ用 */
private boolean markReachedEndOfEntry;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// properties
//------------------------------------------------------------------
// private Properties property
//------------------------------------------------------------------
/**
* 各圧縮形式に対応した復号器の生成式等が含まれるプロパティ
*/
private Properties property;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private LhaInputStream()
// public LhaInputStream( InputStream in )
// public LhaInputStream( InputStream in, Properties property )
// private void constructerHelper( InputStream in, Properties property )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private LhaInputStream(){ }
/**
* in から LHA書庫のデータを読み取る InputStream を構築する。
* 各圧縮形式に対応した復号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param in LHA書庫形式のデータを供給する入力ストリーム
*
* @see LhaProperty#getProperties()
*/
public LhaInputStream( InputStream in ){
Properties property = LhaProperty.getProperties();
try{
this.constructerHelper( in, property ); //After Java 1.1 throws UnsupportedEncodingException
}catch( UnsupportedEncodingException exception ){
throw new Error( "Unsupported encoding \"" + property.getProperty( "lha.encoding" ) + "\"." );
}
}
/**
* in から LHA書庫のデータを読み取る InputStreamを構築する。
*
* @param in LHA書庫形式のデータを供給する入力ストリーム
* @param property 各圧縮形式に対応した復号器の生成式等が含まれるプロパティ
*
* @exception UnsupportedEncodingException
* property.getProperty( "lha.encoding" ) で得られた
* エンコーディング名がサポートされない場合
*/
public LhaInputStream( InputStream in, Properties property )
throws UnsupportedEncodingException {
this.constructerHelper( in, property ); //After Java 1.1 throws UnsupportedEncodingException
}
/**
* コンストラクタの初期化処理を担当するメソッド。
*
* @param in LHA書庫形式のデータを供給する入力ストリーム
* @param property 各圧縮形式に対応した復号器の生成式等が含まれるプロパティ
*
* @exception UnsupportedEncodingException
* encode がサポートされない場合
*/
private void constructerHelper( InputStream in, Properties property )
throws UnsupportedEncodingException {
if( in != null && property != null ){
String encoding = property.getProperty( "lha.encoding" );
if( encoding == null ){
encoding = LhaProperty.getProperty( "lha.encoding" );
}
//encoding名チェック
encoding.getBytes( encoding ); //After Java 1.1 throws UnsupportedEncodingException
if( in.markSupported() ){
this.source = in;
}else{
this.source = new BufferedInputStream( in );
}
this.in = null;
this.limit = null;
this.property = (Properties)property.clone();
this.reachedEndOfEntry = false;
this.reachedEndOfArchive = false;
}else if( in == null ){
throw new NullPointerException( "in" );
}else{
throw new NullPointerException( "property" );
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
// public long skip( long length )
//------------------------------------------------------------------
/**
* 現在のエントリから 1バイトのデータを読み込む。
*
* @return 読みこまれた 1バイトのデータ。
* 既にエントリの終端に達した場合は -1
*
* @exception IOException 現在読み込み中のエントリが無いか
* 入出力エラーが発生した場合
*/
public int read() throws IOException {
if( this.in != null ){
int ret = this.in.read(); //throws IOException
if( ret < 0 ){
this.reachedEndOfEntry = true;
}
return ret;
}else{
throw new IOException( "no entry" );
}
}
/**
* 現在のエントリから buffer を満たすようにデータを読み込む。
*
* @param buffer データを読み込むバッファ
*
* @return 読みこまれたデータの量。
* 既にエントリの終端に達した場合は -1。
*
* @exception IOException 現在読み込み中のエントリが無いか
* 入出力エラーが発生した場合
*/
public int read( byte[] buffer ) throws IOException {
return this.read( buffer, 0, buffer.length ); //throws IOException
}
/**
* 現在のエントリから buffer のindexへ lengthバイトの
* データをを読み込む。
*
* @param buffer データを読み込むバッファ
* @param index buffer内のデータ読み込み開始位置
* @param length 読み込むデータ量
*
* @return 読みこまれたデータの量。
* 既にエントリの終端に達した場合は -1。
*
* @exception IOException 現在読み込み中のエントリが無いか
* 入出力エラーが発生した場合
*/
public int read( byte[] buffer, int index, int length ) throws IOException {
if( this.in != null ){
int ret = this.in.read( buffer, index, length ); //throws IOException
if( ret < 0 ){
this.reachedEndOfEntry = true;
}
return ret;
}else{
throw new IOException( "no entry" );
}
}
/**
* 現在のエントリのデータを length バイト読みとばす。
*
* @param length 読みとばすデータ量
*
* @return 実際に読みとばしたデータ量
*
* @exception IOException 現在読み込み中のエントリが無いか
* 入出力エラーが発生した場合
*/
public long skip( long length ) throws IOException {
if( this.in != null ){
if( 0 < length ){
long len = this.in.skip( length - 1 ); //throws IOException
int ret = this.in.read(); //throws IOException
if( ret < 0 ){
this.reachedEndOfEntry = true;
return len;
}else{
return len + 1;
}
}else{
return 0;
}
}else{
throw new IOException( "no entry" );
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public void mark()
// public void reset()
// public boolean markSupported()
//------------------------------------------------------------------
/**
* 現在読み取り中のエントリの現在位置にマークを設定し、
* reset() でマークした読み込み位置に戻れるようにする。
*
* @param readLimit マーク位置に戻れる限界読み込み量。
* このバイト数を超えてデータを読み込んだ場合
* reset() できる保証はない。
*
* @exception IllegalStateException
* 現在読み込み中のエントリが無い場合
*/
public void mark( int readLimit ){
if( this.in != null ){
this.in.mark( readLimit );
this.markReachedEndOfEntry = this.reachedEndOfEntry;
}else{
throw new IllegalStateException();
}
}
/**
* 現在読み取り中のエントリの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException 現在読み込み中のエントリが無いか
* 入出力エラーが発生した場合
*/
public void reset() throws IOException {
if( this.in != null ){
this.in.reset(); //throws IOException
this.reachedEndOfEntry = this.markReachedEndOfEntry;
}else{
throw new IOException( "no entry" );
}
}
/**
* 接続された入力ストリームが mark()と
* reset()をサポートするかを得る。
* ヘッダ読み込み時に mark/reset が必須のため
* コンストラクタで渡された in が markSupported() で
* false を返す場合、このクラスは in を mark/reset をサポートする
* BufferedInputStream でラップする。
* そのため、このメソッドは常に true を返す。
*
* @return 常に true
*/
public boolean markSupported(){
return this.source.markSupported();
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* 現在読み取り中のエントリの終端に達したかを得る。
* ブロックしないで読み込めるデータ量を返さない事に注意すること。
*
* @return 現在読み取り中のエントリの終端に達した場合 0 達していない場合 1
*
* @exception IOException 現在読み込み中のエントリが無いか
* 入出力エラーが発生した場合
*
* @see java.util.zip.ZipInputStream#available()
*/
public int available() throws IOException {
if( this.in != null ){
return ( this.reachedEndOfEntry ? 0 : 1 );
}else{
throw new IOException( "no entry" );
}
}
/**
* この入力ストリームを閉じ、使用していた
* 全てのリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
if( this.in != null ){
this.in.close();
this.limit = null;
this.in = null;
}
this.source.close();
this.source = null;
}
//------------------------------------------------------------------
// original method ( on the model of java.util.zip.ZipInputStream )
//------------------------------------------------------------------
// manipulate entry
//------------------------------------------------------------------
// public LhaHeader getNextEntry()
// public LhaHeader getNextEntryWithoutExtract()
// public void closeEntry()
//------------------------------------------------------------------
/**
* 次のエントリを解凍しながら読みこむようにストリームを設定する。
*
* @return エントリの情報を持つ LhaHeader
*
* @exception IOException 入出力エラーが発生した場合
*/
public LhaHeader getNextEntry() throws IOException {
if( !this.reachedEndOfArchive ){
if( this.in != null ){
this.closeEntry(); //throws IOException
}
byte[] HeaderData;
if( this.alreadyOpenedFirstEnrty ){
HeaderData = LhaHeader.getNextHeaderData( this.source );
}else{
HeaderData = LhaHeader.getFirstHeaderData( this.source );
this.alreadyOpenedFirstEnrty = true;
}
if( null != HeaderData ){
LhaHeader header = LhaHeader.createInstance( HeaderData, this.property );
this.in = new DisconnectableInputStream( this.source );
this.limit = new LimitedInputStream( this.in, header.getCompressedSize() );
this.in = CompressMethod.connectDecoder( this.limit,
header.getCompressMethod(),
this.property,
header.getOriginalSize() );
this.reachedEndOfEntry = false;
this.markReachedEndOfEntry = false;
return header;
}else{
this.reachedEndOfArchive = true;
return null;
}
}else{
return null;
}
}
/**
* 次のエントリを解凍しないで読みこむようにストリームを設定する。
*
* @return エントリの情報を持つ LhaHeader
*
* @exception IOException 入出力エラーが発生した場合
*/
public LhaHeader getNextEntryWithoutExtract() throws IOException {
if( !this.reachedEndOfArchive ){
if( this.in != null ){
this.closeEntry(); //throws IOException
}
byte[] HeaderData;
if( this.alreadyOpenedFirstEnrty ){
HeaderData = LhaHeader.getNextHeaderData( this.source );
}else{
HeaderData = LhaHeader.getFirstHeaderData( this.source );
this.alreadyOpenedFirstEnrty = true;
}
if( HeaderData != null ){
LhaHeader header = LhaHeader.createInstance( HeaderData, this.property );
this.in = new DisconnectableInputStream( this.source );
this.limit = new LimitedInputStream( this.in, header.getCompressedSize() );
this.in = this.limit;
this.reachedEndOfEntry = false;
this.markReachedEndOfEntry = false;
return header;
}else{
this.reachedEndOfArchive = true;
return null;
}
}else{
return null;
}
}
/**
* 現在読み取り中のエントリを閉じ、
* 次のエントリを読みこめるようにストリームを設定する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void closeEntry() throws IOException {
if( this.in != null ){
while( 0 <= this.limit.read() ){
this.limit.skip( Long.MAX_VALUE );
}
this.in.close();
this.in = null;
this.limit = null;
}
}
}
//end of LhaInputStream.java
jp/gr/java_conf/dangan/util/lha/LhaOutputStream.java 100644 0 0 127122 10235626330 20136 0 ustar 0 0 //start of LhaOutputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LhaOutputStream.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Properties;
import jp.gr.java_conf.dangan.io.GrowthByteBuffer;
import jp.gr.java_conf.dangan.util.lha.CRC16;
import jp.gr.java_conf.dangan.util.lha.LhaHeader;
import jp.gr.java_conf.dangan.util.lha.LhaProperty;
import jp.gr.java_conf.dangan.util.lha.CompressMethod;
//import exceptions
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.Error;
/**
* 接続されたストリームに 圧縮データを出力するためのユーティリティクラス。
* java.util.zip.ZipOutputStream と似たインターフェイスを持つように作った。
* Zipと違い、LHAの出力は本来 2パスであるため、1つのエントリを圧縮するまで、
* エントリ全体のデータを持つ一時記憶領域が必要となる。
* そのような記憶領域を使用したくない場合は LhaRetainedOutputStream か
* LhaImmediateOutputStream を使用する事。
*
*
* -- revision history --
* $Log: LhaOutputStream.java,v $
* Revision 1.1.2.2 2005/05/03 07:48:40 dangan
* [bug fix]
* 圧縮法識別子 -lhd- を指定した時、圧縮後サイズがオリジナルサイズを下回らないため、
* 必ず -lh0- に再設定されていた。そのためディレクトリ情報を格納できなかった。
*
* Revision 1.1.2.1 2005/04/29 02:14:28 dangan
* [bug fix]
* 圧縮法識別子 -lhd- を指定した時、圧縮後サイズがオリジナルサイズを下回らないため、
* 必ず -lh0- に再設定されていた。そのためディレクトリ情報を格納できなかった。
*
* Revision 1.1 2002/12/08 00:00:00 dangan
* [maintenance]
* LhaConstants から CompressMethod へのクラス名の変更に合わせて修正。
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [change]
* コンストラクタから 引数に String encode を取るものを廃止、
* Properties を引数に取るものを追加。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1.2.2 $
*/
public class LhaOutputStream extends OutputStream{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private OutputStream out
//------------------------------------------------------------------
/**
* 圧縮データを出力するストリーム
*/
private OutputStream out;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// to compress a file
//------------------------------------------------------------------
// private CRC16 crc
// private Temporary temp
// private LhaHeader header
// private OutputStream tempOut
// private long length
//------------------------------------------------------------------
/**
* CRC16値算出用クラス
*/
private CRC16 crc;
/**
* 一時記憶用オブジェクト
*/
private Temporary temp;
/**
* 現在圧縮中のエントリのヘッダ
*/
private LhaHeader header;
/**
* 現在圧縮中のエントリの圧縮用出力ストリーム
*/
private OutputStream tempOut;
/**
* 現在圧縮中エントリの圧縮前のデータの長さ
*/
private long length;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// property
//------------------------------------------------------------------
// private Properties property
//------------------------------------------------------------------
/**
* 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*/
private Properties property;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private LhaOutputStream()
// public LhaOutputStream( OutputStream out )
// public LhaOutputStream( OutputStream out, Properties property )
// public LhaOutputStream( OutputStream out, RandomAccessFile file )
// public LhaOutputStream( OutputStream out, RandomAccessFile file,
// Properties property )
// private void constructerHelper( OutputStream out, Temporary temp,
// Properties property )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ
* 使用不可
*/
private LhaOutputStream(){ }
/**
* out に 圧縮データを出力するOutputStreamを構築する。
* 一時退避機構はメモリを使用する。このため、
* 圧縮時データ量がメモリ量を超えるようなファイルは圧縮できない。
* 各圧縮形式に対応した符号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param out 圧縮データを出力するストリーム
*
* @see LhaProperty#getProperties()
*/
public LhaOutputStream( OutputStream out ){
if( out != null ){
Properties property = LhaProperty.getProperties();
this.constructerHelper( out, new TemporaryBuffer(), property ); //throws UnsupportedEncodingException
}else{
throw new NullPointerException( "out" );
}
}
/**
* out に 圧縮データを出力するOutputStreamを構築する。
* 一時退避機構はメモリを使用する。このため、
* 圧縮時データ量がメモリ量を超えるようなファイルは圧縮できない。
*
* @param out 圧縮データを出力するストリーム
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*
* @see LhaProperty
*/
public LhaOutputStream( OutputStream out, Properties property ){
if( out != null
&& property != null ){
this.constructerHelper( out, new TemporaryBuffer(), property ); //throws UnsupportedEncodingException
}else if( out == null ){
throw new NullPointerException( "out" );
}else{
throw new NullPointerException( "property" );
}
}
/**
* out に 圧縮データを出力するOutputStreamを構築する。
* 各圧縮形式に対応した符号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param out 圧縮データを出力するストリーム
* @param file RandomAccessFile のインスタンス。
*
* - 既に close() されていない事。
*
- コンストラクタの mode には "rw" オプションを使用して、
* 読みこみと書きこみが出来るように生成されたインスタンスであること。
*
* の条件を満たすもの。
*
* @see LhaProperty#getProperties()
*/
public LhaOutputStream( OutputStream out, RandomAccessFile file ){
if( out != null
&& file != null ){
Properties property = LhaProperty.getProperties();
this.constructerHelper( out, new TemporaryFile( file ), property ); //throws UnsupportedEncodingException
}else if( out == null ){
throw new NullPointerException( "out" );
}else{
throw new NullPointerException( "file" );
}
}
/**
* out に 圧縮データを出力するOutputStreamを構築する。
*
* @param out 圧縮データを出力するストリーム
* @param file RandomAccessFile のインスタンス。
*
* - 既に close() されていない事。
*
- コンストラクタの mode には "rw" オプションを使用して、
* 読みこみと書きこみが出来るように生成されたインスタンスであること。
*
* の条件を満たすもの。
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*
* @exception UnsupportedEncodingException
* encode がサポートされない場合
*
* @see LhaProperty
*/
public LhaOutputStream( OutputStream out,
RandomAccessFile file,
Properties property ){
if( out != null
&& file != null
&& property != null ){
this.constructerHelper( out, new TemporaryFile( file ), property ); //throws UnsupportedEncodingException
}else if( out == null ){
throw new NullPointerException( "out" );
}else if( file == null ){
throw new NullPointerException( "file" );
}else{
throw new NullPointerException( "property" );
}
}
/**
* コンストラクタの初期化処理を担当するメソッド。
*
* @param out LHA書庫形式のデータを出力する出力ストリーム
* @param temp 圧縮データの一時退避機構
* @param encode ヘッダ内の文字列を変換するのに使用する
* エンコード日本では シフトJIS(SJIS,MS932,
* CP932等)を使用する事
*
* @exception UnsupportedEncodingException
* encode がサポートされない場合
*/
private void constructerHelper( OutputStream out,
Temporary temp,
Properties property ){
this.out = out;
this.temp = temp;
this.property = property;
this.crc = new CRC16();
this.header = null;
this.tempOut = null;
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* 現在のエントリに1バイトのデータを書きこむ。
*
* @param data 書きこむデータ
*
* @exception IOException 入出力エラーが発生した場合。
*/
public void write( int data ) throws IOException {
if( this.tempOut != null ){
if( this.header != null ){
crc.update( data );
}
this.tempOut.write( data );
this.length++;
}else{
throw new IOException( "no entry" );
}
}
/**
* 現在のエントリに bufferの内容を全て書き出す。
*
* @param buffer 書き出すデータの入ったバイト配列
*
* @exception IOException 入出力エラーが発生した場合。
*/
public void write( byte[] buffer ) throws IOException {
this.write( buffer, 0, buffer.length );
}
/**
* 現在のエントリに bufferの indexから lengthバイトのデータを書き出す。
*
* @param buffer 書き出すデータの入ったバイト配列
* @param index buffer内の書き出すべきデータの開始位置
* @param length データのバイト数
*
* @exception IOException 入出力エラーが発生した場合。
*/
public void write( byte[] buffer, int index, int length ) throws IOException {
if( this.tempOut != null ){
if( this.header != null ){
crc.update( buffer, index, length );
}
this.tempOut.write( buffer, index, length );
this.length += length;
}else{
throw new IOException( "no entry" );
}
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* flush は二つの動作を行う。
* 一つは現在書き込み中のエントリのデータを
* 一時退避機構に送りこむように指示する。
* これは PostLzssDecoder、LzssOutputStream
* の規約どおり flush() しなかった場合と
* 同じデータが出力される事を保証しない。
* もう一つは 実際の出力先を flush() する。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PostLzssEncoder#flush()
* @see LzssOutputStream#flush()
*/
public void flush() throws IOException {
if( this.tempOut != null ){
this.tempOut.flush(); //throws IOException
}
if( this.tempOut != this.out ){
this.out.flush(); //throws IOException
}
}
/**
* 出力先に全てのデータを出力し、
* ストリームを閉じる。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
if( this.tempOut != null ){
this.closeEntry(); //throws IOException
}
//ターミネータを出力
this.out.write( 0 ); //throws IOException
this.out.close(); //throws IOException
this.out = null;
this.temp.close();
this.temp = null;
this.property = null;
this.crc = null;
this.header = null;
}
//------------------------------------------------------------------
// original method ( on the model of java.util.zip.ZipOutputStream )
//------------------------------------------------------------------
// manipulate entry
//------------------------------------------------------------------
// public void putNextEntry( LhaHeader header )
// public void putNextEntryAlreadyCompressed( LhaHeader header )
// public void putNextEntryNotYetCompressed( LhaHeader header )
// public void closeEntry()
//------------------------------------------------------------------
/**
* 新しいエントリを書き込むようにストリームを設定する。
* このメソッドは 既に圧縮済みのエントリの場合は
* putNextEntryAlreadyCompressed(),
* 未だに圧縮されていない場合は
* putNextEntryNotYetCompressed() を呼び出す。
* 圧縮されているかの判定は、
*
* - header.getCompressedSize()
* - header.getOriginalSize()
* - header.getCRC()
*
* のどれか一つでも LhaHeader.UNKNOWN であれば未だに圧縮されていないとする。
*
* @param header 書きこむエントリについての情報を持つ
* LhaHeaderのインスタンス。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void putNextEntry( LhaHeader header ) throws IOException {
if( header.getCompressedSize() == LhaHeader.UNKNOWN
|| header.getOriginalSize() == LhaHeader.UNKNOWN
|| header.getCRC() == LhaHeader.UNKNOWN ){
this.putNextEntryNotYetCompressed( header ); //throws IOException
}else{
this.putNextEntryAlreadyCompressed( header ); //throws IOException
}
}
/**
* 既に圧縮済みのエントリを書きこむようにストリームを設定する。
* 圧縮済みなので、一時退避機構を経ずに直接出力先に出力される。
* 圧縮済みデータが正しい事は、呼び出し側が保証する事。
*
* @param header 書きこむエントリについての情報を持つ
* LhaHeaderのインスタンス。
*
* @exception IOException 入出力エラーが発生した場合
* @exception IllegalArgumentException
*
* - header.getOriginalSize() が LhaHeader.UNKNOWN を返す場合
*
- header.getComressedSize() が LhaHeader.UNKNOWN を返す場合
*
- header.getCRC() が LhaHeader.UNKNOWN を返す場合
*
* の何れか。
*/
public void putNextEntryAlreadyCompressed( LhaHeader header ) throws IOException {
if( header.getOriginalSize() != LhaHeader.UNKNOWN
&& header.getCompressedSize() != LhaHeader.UNKNOWN
&& header.getCRC() != LhaHeader.UNKNOWN ){
if( this.tempOut != null ){
this.closeEntry(); //throws IOException
}
String encoding = this.property.getProperty( "lha.encoding" );
if( encoding == null ){
encoding = LhaProperty.getProperty( "lha.encoding" );
}
this.out.write( header.getBytes( encoding ) ); //throws IOException
this.tempOut = out;
}else if( header.getOriginalSize() == LhaHeader.UNKNOWN ){
throw new IllegalArgumentException( "OriginalSize must not \"LhaHeader.UNKNOWN\"." );
}else if( header.getCompressedSize() == LhaHeader.UNKNOWN ){
throw new IllegalArgumentException( "CompressedSize must not \"LhaHeader.UNKNOWN\"." );
}else{
throw new IllegalArgumentException( "CRC must not \"LhaHeader.UNKNOWN\"." );
}
}
/**
* 未だに圧縮されていないエントリを書きこむようにストリームを設定する。
* header に OriginalSize, CompressedSize, CRCが指定されていても無視される。
*
* @param header 書きこむエントリについての情報を持つ
* LhaHeaderのインスタンス。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void putNextEntryNotYetCompressed( LhaHeader header ) throws IOException {
if( this.tempOut != null ){
this.closeEntry(); //throws IOException
}
this.crc.reset();
this.length = 0;
this.header = (LhaHeader)header.clone();
this.tempOut = CompressMethod.connectEncoder( this.temp.getOutputStream(),
header.getCompressMethod(),
this.property );
}
/**
* 現在出力中のエントリを閉じ、次のエントリが出力可能な状態にする。
* 圧縮に失敗した(圧縮後サイズが圧縮前サイズを上回った)場合、
* 解凍し無圧縮で格納する。エントリのサイズが大きい場合、
* この処理にはかなりの時間がかかる。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void closeEntry() throws IOException {
if( this.header != null ){
this.tempOut.close();
InputStream in;
if( this.temp.length() < this.length ){
this.header.setOriginalSize( this.length );
this.header.setCompressedSize( this.temp.length() );
this.header.setCRC( (int)crc.getValue() );
in = this.temp.getInputStream(); //throws IOException
}else{
String method = this.header.getCompressMethod();
this.header.setOriginalSize( this.length );
this.header.setCompressedSize( this.length );
this.header.setCRC( (int)crc.getValue() );
if( !this.header.getCompressMethod().equalsIgnoreCase( CompressMethod.LHD ) ){
this.header.setCompressMethod( CompressMethod.LH0 );
}
in = this.temp.getInputStream(); //throws IOException
in = CompressMethod.connectDecoder( in,
method,
this.property,
this.temp.length() );
}
String encoding = this.property.getProperty( "lha.encoding" );
if( encoding == null ){
encoding = LhaProperty.getProperty( "lha.encoding" );
}
this.out.write( this.header.getBytes( encoding ) ); //throws UnsupportedEncodingException, IOException
byte[] buffer = new byte[ 8192 ];
int length;
while( 0 <= ( length = in.read( buffer ) ) ){ //throws IOException
this.out.write( buffer, 0, length ); //throws IOException
}
}
this.header = null;
this.tempOut = null;
}
//------------------------------------------------------------------
// inner class
//------------------------------------------------------------------
// private static interface Temporary
// private static class TemporaryFile
// private static class TemporaryBuffer
//------------------------------------------------------------------
/**
* データの一時退避機構を提供する。
*/
private static interface Temporary{
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// public abstract InputStream getInputStream()
// public abstract OutputStream getOutputStream()
// public abstract long length()
// public abstract void close()
//------------------------------------------------------------------
/**
* 一時退避機構に貯えられたデータを取り出すInputStream を得る。
* このデータは直前の getOutputStream() で与えられる
* OutputStream に出力されたデータと同じである。
* getInputStream() で得られた InputStream が close() されるまで、
* getOutputStream() を呼んではならない。
* また、getInputStream() で得られた InputStream が close() されるまで、
* 再び getInputStream() を呼んではならない。
*
* @return 一時退避機構からデータを取り出す InputStream
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract InputStream getInputStream() throws IOException;
/**
* データを一時退避機構に貯えるOutputStream を得る。
* 貯えたデータは直後の getInputStream() で得られる
* InputStream から得る事が出来る。
* getOutputStream で得られた OutputStream が close() されるまで、
* getInputStream() を呼んではならない。
* また、getOutputStream() で得られた OutputStream が close() されるまで、
* 再び getOutputStream() を呼んではならない。
*
* @return データを一時退避機構に貯える OutputStream
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract OutputStream getOutputStream() throws IOException;
/**
* 一時退避機構に格納されているデータ量を得る。
* これは 直前の getOutputStream() で与えられた
* OutputStream に出力されたデータ量と同じである。
*
* @return 一時退避機構に格納されているデータ量
*/
public abstract long length() throws IOException;
/**
* 一時退避機構で使用されていた、全てのシステムリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract void close() throws IOException ;
}
/**
* 一時退避機構に RandomAccessFile を使用するクラス。
*/
private static class TemporaryFile implements Temporary{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private RandomAccessFile tempfile
// private long length
//------------------------------------------------------------------
/**
* 一時退避機構に使用する RandomAccessFile
*/
private RandomAccessFile tempfile;
/**
* getOutputStream で与えた OutputStream に出力されたデータ量を保持する。
*/
private long length;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public TemporaryFile( RandomAccessFile file )
//------------------------------------------------------------------
/**
* コンストラクタ fileを使用して TemporaryFile を構築する。
*
* @param file RandomAccessFile のインスタンス
*/
public TemporaryFile( RandomAccessFile file ){
if( file != null ){
this.tempfile = file;
}else{
throw new NullPointerException( "file" );
}
}
//------------------------------------------------------------------
// method of Temporary
//------------------------------------------------------------------
// public InputStream getInputStream()
// public OutputStream getOutputStream()
// public long length()
// public void close()
//------------------------------------------------------------------
/**
* 一時退避機構に貯えられたデータを取り出す InputStream を得る。
* このデータは直前の getOutputStream() で与えられる
* OutputStream に出力されたデータと同じ。
*
* @return 一時退避機構からデータを取り出す InputStream
*
* @exception IOException 入出力エラーが発生した場合
*/
public InputStream getInputStream() throws IOException {
return new TemporaryFileInputStream();
}
/**
* データを一時退避機構に貯えるOutputStreamを得る。
* 貯えたデータは直後の getInputStream() で
* 得られる InputStream から得る事が出来る。
*
* @return データを一時退避機構に貯える OutputStream
*
* @exception IOException 入出力エラーが発生した場合
*/
public OutputStream getOutputStream() throws IOException {
return new TemporaryFileOutputStream();
}
/**
* 一時退避機構に格納されているデータ量を得る。
* これは 直前の getOutputStream() で与えられた
* OutputStream に出力されたデータ量と同じである。
*
* @return 一時退避機構に格納されているデータ量
*/
public long length(){
return this.length;
}
/**
* 一時退避機構で使用されていた、全てのシステムリソースを開放する。
* コンストラクタで与えられた RandomAccessFile は閉じられる。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.tempfile.close(); //throws IOException
this.tempfile = null;
}
//------------------------------------------------------------------
// inner classes
//------------------------------------------------------------------
// private class TemporaryFileInputStream
// private class TemporaryFileOutputStream
//------------------------------------------------------------------
/**
* TemporaryFile の入力ストリーム
*/
private class TemporaryFileInputStream extends InputStream {
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public TemporaryFileInputStream()
//------------------------------------------------------------------
/**
* TemporaryFile からデータを読み込む InputStream を構築する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public TemporaryFileInputStream() throws IOException {
TemporaryFile.this.tempfile.seek( 0 ); //throws IOException
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* TemporaryFileから 1バイトのデータを読み込む。
*
* @return 読みこまれた1バイトのデータ
* 既にEndOfStreamに達している場合は-1
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read() throws IOException {
long pos = TemporaryFile.this.tempfile.getFilePointer(); //throws IOException
long limit = TemporaryFile.this.length;
if( pos < limit ){
return TemporaryFile.this.tempfile.read(); //throws IOException
}else{
return -1;
}
}
/**
* TemporaryFileから bufferを満たすようにデータを読み込む。
*
* @param buffer データを読み込むバッファ
*
* @return 読みこまれたデータ量
* 既にEndOfStreamに達している場合は-1
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer ) throws IOException {
return this.read( buffer, 0, buffer.length ); //throws IOException
}
/**
* TemporaryFileから bufferの indexへlengthバイトのデータを読み込む
*
* @param buffer データを読み込むバッファ
* @param index buffer内のデータ読みこみ開始位置
* @param length 読み込むデータ量
*
* @return 読みこまれたデータ量
* 既にEndOfStreamに達している場合は-1
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer, int index, int length )
throws IOException {
long pos = TemporaryFile.this.tempfile.getFilePointer(); //throws IOException
long limit = TemporaryFile.this.length;
length = (int)( Math.min( pos + length, limit ) - pos );
if( pos < limit ){
return TemporaryFile.this.tempfile.read( buffer, index, length );//throws IOException
}else{
return -1;
}
}
}
/**
* TemporaryFile の出力ストリーム
*/
private class TemporaryFileOutputStream extends OutputStream {
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public TemporaryFileOutputStream()
//------------------------------------------------------------------
/**
* TemporaryFile にデータを出力する OutputStream を構築する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public TemporaryFileOutputStream() throws IOException {
TemporaryFile.this.tempfile.seek( 0 ); //throws IOException
TemporaryFile.this.length = 0;
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* TemporaryFile に 1byteのデータを書き出す。
*
* @param data 書き出す1byteのデータ
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( int data ) throws IOException {
TemporaryFile.this.tempfile.write( data ); //throws IOException
TemporaryFile.this.length++;
}
/**
* TemporaryFile に bufferの内容を全て書き出す。
*
* @param buffer 書き出すデータの入ったバイト配列
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer ) throws IOException {
TemporaryFile.this.tempfile.write( buffer ); //throws IOException
TemporaryFile.this.length += buffer.length;
}
/**
* TemporaryFile に bufferのindex からlengthバイトの内容を書き出す。
*
* @param buffer 書き出すデータの入ったバイト配列
* @param index buffer内の書き出すデータの開始位置
* @param length 書き出すデータ量
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer, int index, int length )
throws IOException {
TemporaryFile.this.tempfile.write( buffer, index, length ); //throws IOException
TemporaryFile.this.length += length;
}
}
}
/**
* 一時退避機構に GrowthByteBufferを使用するクラス
*/
private static class TemporaryBuffer implements Temporary {
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private GrowthByteBuffer tempbuffer
//------------------------------------------------------------------
/**
* 一時退避機構に使用されるバッファ
*/
private GrowthByteBuffer tempbuffer;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public TemporaryBuffer()
//------------------------------------------------------------------
/**
* GrowthByteBuffer を使用した検索機構を構築する。
*/
public TemporaryBuffer(){
this.tempbuffer = new GrowthByteBuffer();
}
//------------------------------------------------------------------
// method of Temporary
//------------------------------------------------------------------
// public InputStream getInputStream()
// public OutputStream getOutputStream()
// public long length()
// public void close()
//------------------------------------------------------------------
/**
* 一時退避機構に貯えられたデータを取り出す InputStream を得る。
* このデータは直前の getOutputStream() で与えられる
* OutputStream に出力されたデータと同じ。
*
* @return 一時退避機構からデータを取り出す InputStream
*/
public InputStream getInputStream(){
return new TemporaryBufferInputStream();
}
/**
* データを一時退避機構に貯える OutputStream を得る。
* 貯えたデータは直後の getInputStream() で得られる
* InputStream から得る事が出来る。
*
* @return データを一時退避機構に貯える OutputStream
*/
public OutputStream getOutputStream(){
return new TemporaryBufferOutputStream();
}
/**
* 一時退避機構に格納されているデータ量を得る。
* これは 直前の getOutputStream() で与えた
* OutputStream に出力されたデータ量と同じである。
*
* @return 一時退避機構に格納されているデータ量
*/
public long length(){
return this.tempbuffer.length();
}
/**
* 一時退避機構で使用されていた、全てのシステムリソースを開放する。
*/
public void close(){
this.tempbuffer = null;
}
//------------------------------------------------------------------
// inner classes
//------------------------------------------------------------------
// private class TemporaryBufferInputStream
// private class TemporaryBufferOutputStream
//------------------------------------------------------------------
/**
* TemporaryBuffer の入力ストリーム
*/
private class TemporaryBufferInputStream extends InputStream{
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public TemporaryBufferInputStream()
//------------------------------------------------------------------
/**
* TemporaryBuffer からデータを読み込む InputStream を構築する。
*/
public TemporaryBufferInputStream(){
TemporaryBuffer.this.tempbuffer.seek( 0 );
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* TemporaryBuffer から 1バイトのデータを読み込む。
*
* @return 読みこまれた1バイトのデータ
* 既にEndOfStreamに達している場合は-1
*/
public int read(){
return TemporaryBuffer.this.tempbuffer.read();
}
/**
* TemporaryBuffer から bufferを満たすようにデータを読み込む。
*
* @param buffer データを読み込むバッファ
*
* @return 読みこまれたデータ量
* 既にEndOfStreamに達している場合は-1
*/
public int read( byte[] buffer ){
return TemporaryBuffer.this.tempbuffer.read( buffer );
}
/**
* TemporaryBuffer から bufferの indexへ lengthバイトのデータを読み込む
*
* @param buffer データを読み込むバッファ
* @param index buffer内のデータ読みこみ開始位置
* @param length 読み込むデータ量
*
* @return 読みこまれたデータ量
* 既にEndOfStreamに達している場合は-1
*/
public int read( byte[] buffer, int index, int length ){
return TemporaryBuffer.this.tempbuffer.read( buffer, index, length );
}
}
/**
* TemporaryBuffer の出力ストリーム
*/
private class TemporaryBufferOutputStream extends OutputStream {
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public TemporaryBufferOutputStream()
//------------------------------------------------------------------
/**
* TemporaryBuffer にデータを出力する OutputStream を構築する。
*/
public TemporaryBufferOutputStream(){
TemporaryBuffer.this.tempbuffer.seek( 0 );
TemporaryBuffer.this.tempbuffer.setLength( 0 );
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* TemporaryBuffer に 1byteのデータを書き出す。
*
* @param data 書き出す1byteのデータ
*/
public void write( int data ){
TemporaryBuffer.this.tempbuffer.write( data );
}
/**
* TemporaryBuffer に bufferの内容を全て書き出す。
*
* @param buffer 書き出すデータの入ったバイト配列
*/
public void write( byte[] buffer ){
TemporaryBuffer.this.tempbuffer.write( buffer );
}
/**
* TemporaryBuffer に bufferのindex から lengthバイトの内容を書き出す。
*
* @param buffer 書き出すデータの入ったバイト配列
* @param index buffer内の書き出すデータの開始位置
* @param length 書き出すデータ量
*/
public void write( byte[] buffer, int index, int length ){
TemporaryBuffer.this.tempbuffer.write( buffer, index, length );
}
}
}
}
//end of LhaOutputStream.java
jp/gr/java_conf/dangan/util/lha/LhaProperty.java 100644 0 0 105474 10234314331 17306 0 ustar 0 0 //start of LhaProperty.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LhaProperty.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.util.Stack;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Enumeration;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import jp.gr.java_conf.dangan.lang.reflect.Factory;
//import exceptions
import java.lang.RuntimeException;
import java.lang.SecurityException;
import java.lang.NoSuchMethodException;
import java.lang.ClassNotFoundException;
import java.lang.InstantiationException;
import java.lang.reflect.InvocationTargetException;
import java.util.MissingResourceException;
import java.lang.Error;
import java.lang.NoSuchMethodError;
import java.lang.InstantiationError;
import java.lang.NoClassDefFoundError;
/**
* LHA Library for Java の各種設定を扱う。
* LhaProperty.getProperty() や LhaProperty.getProperties() で得られる値は
* システムプロパティ、設定ファイル、デフォルト値の何れかが用いられ、
* その優先順位は以下のようになる。
*
* - システムプロパティ に設定されている値。
*
- jp/gr/java_conf/dangan/util/lha/resources/lha.properties
* に設定された値。
*
- デフォルト値。
*
*
*
* キーの一覧は以下のとおり。
*
*
*
* キー |
* 対応する値の説明 |
*
*
* lha.encoding |
* String とヘッダ内の文字列との相互変換に用いるエンコーディング |
*
*
* lha.packages |
* 生成式内で使われるクラスのパッケージ名の列挙(カンマ区切り) |
*
*
* lha.lzs.encoder |
* -lzs- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lz4.encoder |
* -lz4- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lz5.encoder |
* -lz5- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lh0.encoder |
* -lh0- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lh1.encoder |
* -lh1- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lh2.encoder |
* -lh2- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lh3.encoder |
* -lh3- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lh4.encoder |
* -lh4- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lh5.encoder |
* -lh5- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lh6.encoder |
* -lh6- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lh7.encoder |
* -lh7- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lhd.encoder |
* -lhd- 形式への符号化を行うオブジェクトの生成式 |
*
*
* lha.lzs.decoder |
* -lzs- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lz4.decoder |
* -lz4- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lz5.decoder |
* -lz5- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lh0.decoder |
* -lh0- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lh1.decoder |
* -lh1- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lh2.decoder |
* -lh2- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lh3.decoder |
* -lh3- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lh4.decoder |
* -lh4- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lh5.decoder |
* -lh5- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lh6.decoder |
* -lh6- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lh7.decoder |
* -lh7- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.lhd.decoder |
* -lhd- 形式のデータを復号化するオブジェクトの生成式 |
*
*
* lha.header |
* LhaHeader のインスタンスの生成式 |
*
*
*
* 生成式は以下のように定義される。
*
*
* <生成式> |
* ::= <コンストラクタ> | <配列> | <置換文字列> | <クラス名> | <文字列> |
*
*
* <コンストラクタ> |
* ::= <クラス名> '(' 引数 ')' |
*
*
* <引数> |
* ::= [ <生成式> [ ',' <引数> ] ] |
*
*
* <配列> |
* ::= '[' <要素> ']' |
*
*
* <要素> |
* ::= [ <生成式> [ ',' <要素> ] ] |
*
*
*
* クラス名は "lha.packages" に対応する値を使用して完全修飾名へと変換される。
* 置換文字列 はライブラリ内部でオブジェクトに置換される文字列で
* 現在以下の4種類が定義されている。
*
*
* lha.???.encoder |
* out |
* 圧縮後のデータを受け取る java.io.OutputStream |
*
*
* lha.???.decoder |
* in |
* 圧縮データを供給する java.io.InputStream |
*
*
* length |
* 復号化されたデータのバイト数 |
*
*
* lha.header |
* data |
* ヘッダデータを格納した byte配列 |
*
*
* encoding |
* ヘッダ内の文字データを String に変換する際に使用するエンコーディング |
*
*
*
*
* -- revision history --
* $Log: LhaProperty.java,v $
* Revision 1.0.2.2 2005/04/29 02:15:53 dangan
* [bug fix]
* createDefaultProperty() で圧縮法識別子 -lhd- 用のエンコーダどデコーダが設定されていなかった。
*
* Revision 1.0.2.1 2004/06/27 12:09:49 dangan
* [bugfix]
* 生成式でカンマを使うべき部分でピリオドを使っていたのを修正。
*
* Revision 1.0 2002/12/05 00:00:00 dangan
* first edition
* add to version control
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0.2.2 $
*/
public class LhaProperty {
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// private static final Properties property
// public static final String encoding
//------------------------------------------------------------------
/**
* LHA Library for Java の設定を保持するプロパティ
*/
private static final Properties property = LhaProperty.createLhaProperty();
/**
* LHA Library for Java 内 で
* デフォルトで使用されるエンコーディング
*/
public static final String encoding = LhaProperty.property.getProperty( "lha.encoding" );
//------------------------------------------------------------------
// constructpr
//------------------------------------------------------------------
// private LhaProperty()
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ使用不可
*/
private LhaProperty(){ }
//------------------------------------------------------------------
// access method
//------------------------------------------------------------------
// public static String getProperty( String key )
// public static Properties getProperties()
//------------------------------------------------------------------
/**
* LHA Library for Java のプロパティから
* key に対応するものを取得する。
*
* @param key プロパティのキー
*
* @return ブロパティの文字列
*/
public static String getProperty( String key ){
String def = LhaProperty.property.getProperty( key );
try{
if( key.equals( "lha.encoding" )
&& System.getProperty( key, def ).equals( "ShiftJISAuto" ) ){
try{
String encoding = System.getProperty( "file.encoding" );
if( LhaProperty.isCategoryOfShiftJIS( encoding ) ){
return encoding;
}else{
return "SJIS";
}
}catch( SecurityException exception ){
return "SJIS";
}
}else{
return System.getProperty( key, def );
}
}catch( SecurityException exception ){
}
return def;
}
/**
* LHA Library for Java のプロパティのコピーを得る。
*
* @return プロパティのコピー
*/
public static Properties getProperties(){
Properties property = (Properties)LhaProperty.property.clone();
Enumeration enumkey = property.propertyNames();
while( enumkey.hasMoreElements() ){
String key = (String)enumkey.nextElement();
try{
String val = System.getProperty( key );
if( null != val ){
property.put( key, val );
}
}catch( SecurityException exception ){
}
}
if( property.getProperty( "lha.encoding" ).equals( "ShiftJISAuto" ) ){
try{
String encoding = System.getProperty( "file.encoding" );
if( LhaProperty.isCategoryOfShiftJIS( encoding ) ){
property.put( "lha.encoding", encoding );
}else{
property.put( "lha.encoding", "SJIS" );
}
}catch( SecurityException exception ){
property.put( "lha.encoding", "SJIS" );
}
}
return property;
}
//------------------------------------------------------------------
// parse
//------------------------------------------------------------------
// public static Object parse( String source,
// Hashtable substitute, String packages )
// public static Object parse( String source,
// Hashtable substitute, String[] packages )
// private static Object parseConstructor( String source,
// Hashtable substitute, String[] packages )
// private static Object[] parseArray( String source,
// Hashtable substitute, String[] packages )
// private static String applyPackages( String str, String[] packages )
//------------------------------------------------------------------
/**
* LHA Library for Java のプロパティ用の
* 生成式 source を解析して 新しい Object を生成する。
*
* @param souce 解析すべき生成式
* @param substitute 置換対象文字列をkeyにもち、置換するObjectを値に持つ Hashtable
* @param packages カンマで区切られたパッケージ名の列挙
*
* @return 生成された Object
*/
public static Object parse( String source,
Hashtable substitute,
String packages ){
StringTokenizer tokenizer = new StringTokenizer( packages, "," );
String[] packageArray = new String[ tokenizer.countTokens() ];
int i = 0;
while( tokenizer.hasMoreTokens() ){
packageArray[i++] = tokenizer.nextToken().trim();
}
return LhaProperty.parse( source, substitute, packageArray );
}
/**
* LHA Library for Java のプロパティ用の
* 生成式 source を解析して 新しい Object を生成する。
*
* @param souce 解析すべき文字列
* @param substitute 置換対象文字列をkeyにもち、置換するObjectを値に持つ Hashtable
* @param packages パッケージ名の配列
*
* @return 生成された Object
*/
public static Object parse( String source,
Hashtable substitute,
String[] packages ){
source = source.trim();
int casearcpos = source.indexOf( "(" );
int bracepos = source.indexOf( "[" );
if( 0 <= casearcpos && ( bracepos < 0 || casearcpos < bracepos ) ){
return LhaProperty.parseConstructor( source, substitute, packages );
}else if( 0 <= bracepos && ( casearcpos < 0 || bracepos < casearcpos ) ){
return LhaProperty.parseArray( source, substitute, packages );
}else if( substitute.containsKey( source ) ){
return substitute.get( source );
}else{
return LhaProperty.applyPackages( source, packages );
}
}
/**
* LHA Library for Java のプロパティ用の
* コンストラクタを示す文字列 source を解析して、
* 新しい インスタンスを生成する。
*
* @param souce 解析すべきコンストラクタを示す文字列
* @param substitute 置換対象文字列をkeyにもち、置換するObjectを値に持つ Hashtable
* @param packages パッケージ名の配列
*
* @return 生成されたインスタンス
*/
private static Object parseConstructor( String source,
Hashtable substitute,
String[] packages ){
String classname = source.substring( 0, source.indexOf( '(' ) ).trim();
String arguments = source.substring( source.indexOf( '(' ) + 1,
source.lastIndexOf( ')' ) ).trim();
classname = LhaProperty.applyPackages( classname, packages );
Object[] args;
if( !arguments.equals( "" ) ){
StringTokenizer tokenizer = new StringTokenizer( arguments, ",()[]", true );
Stack stack = new Stack();
int pos = 0;
while( tokenizer.hasMoreTokens() ){
String token = tokenizer.nextToken();
if( token.equals( "(" ) ){
stack.push( "(" );
}else if( token.equals( ")" ) ){
if( !stack.empty() && stack.peek().equals( "(" ) ){
stack.pop();
}
}else if( token.equals( "[" ) ){
stack.push( "[" );
}else if( token.equals( "]" ) ){
if( !stack.empty() && stack.peek().equals( "[" ) ){
stack.pop();
}
}else if( token.equals( "," ) ){
if( stack.empty()
|| ( !stack.peek().equals( "(" )
&& !stack.peek().equals( "[" ) ) ){
stack.push( new Integer( pos ) );
}
}
pos += token.length();
}
pos = 0;
args = new Object[ stack.size() + 1 ];
for( int i = 0 ; i < stack.size() + 1 ; i++ ){
String arg;
if( i < stack.size() ){
arg = arguments.substring( pos, ((Integer)stack.elementAt(i)).intValue() );
}else{
arg = arguments.substring( pos );
}
pos += arg.length() + 1;
args[i] = LhaProperty.parse( arg, substitute, packages );
}
}else{
args = new Object[0];
}
try{
return Factory.createInstance( classname, args );
}catch( InstantiationException exception ){
throw new InstantiationError( exception.getMessage() );
}catch( InvocationTargetException exception ){
if( exception.getTargetException() instanceof RuntimeException ){
throw (RuntimeException)exception.getTargetException();
}else if( exception.getTargetException() instanceof Error ){
throw (Error)exception.getTargetException();
}else{
throw new Error( exception.getTargetException().getMessage() );
}
}catch( ClassNotFoundException exception ){
throw new NoClassDefFoundError( exception.getMessage() );
}catch( NoSuchMethodException exception ){
throw new NoSuchMethodError( exception.getMessage() );
}
}
/**
* LHA Library for Java のプロパティ用の
* 配列を示す文字列 source を解析して、
* 新しい Object の配列を生成する。
*
* @param souce 解析すべきコンストラクタを示す文字列
* @param substitute 置換対象文字列をkeyにもち、置換するObjectを値に持つ Hashtable
* @param packages パッケージ名の配列
*
* @return 生成された Object の配列
*/
private static Object[] parseArray( String source,
Hashtable substitute,
String[] packages ){
String arguments = source.substring( source.indexOf( '[' ) + 1,
source.lastIndexOf( ']' ) ).trim();
if( !arguments.equals( "" ) ){
StringTokenizer tokenizer = new StringTokenizer( arguments, ",()[]", true );
Stack stack = new Stack();
int pos = 0;
while( tokenizer.hasMoreTokens() ){
String token = tokenizer.nextToken();
if( token.equals( "(" ) ){
stack.push( "(" );
}else if( token.equals( ")" ) ){
if( !stack.empty() && stack.peek().equals( "(" ) )
stack.pop();
}else if( token.equals( "[" ) ){
stack.push( "[" );
}else if( token.equals( "]" ) ){
if( !stack.empty() && stack.peek().equals( "[" ) )
stack.pop();
}else if( token.equals( "," ) ){
if( stack.empty()
|| ( !stack.peek().equals( "(" )
&& !stack.peek().equals( "[" ) ) )
stack.push( new Integer( pos ) );
}
pos += token.length();
}
pos = 0;
Object[] array = new Object[ stack.size() + 1 ];
for( int i = 0 ; i < stack.size() + 1 ; i++ ){
String arg;
if( i < stack.size() ){
arg = arguments.substring( pos, ((Integer)stack.elementAt(i)).intValue() );
}else{
arg = arguments.substring( pos );
}
pos += arg.length() + 1;
array[i] = LhaProperty.parse( arg, substitute, packages );
}
return array;
}else{
return new Object[0];
}
}
/**
* str をクラス名だと仮定して packages に含まれるパッケージ名と
* 連結して完全修飾名を作成する事を試みる。
*
* @param str クラス名かもしれない文字列
* @param packages パッケージ名の列挙
*
* @return 完全修飾名、もしくは str
*/
private static String applyPackages( String str, String[] packages ){
for( int i = 0 ; i < packages.length ; i++ ){
String classname;
if( packages[i].equals( "" ) ){
classname = str;
}else{
classname = packages[i] + "." + str;
}
try{
Class.forName( classname );
return classname;
}catch( ClassNotFoundException exception ){
}catch( LinkageError error ){
}
}
return str;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// create property
//------------------------------------------------------------------
// private static final Properties createLhaProperty()
// private static final Properties createDefaultProperty()
//------------------------------------------------------------------
/**
* LHA Library for Java のプロパティを生成する。
*
* @return 生成されたプロパティ
*/
private static final Properties createLhaProperty(){
String path = "jp.gr.java_conf.dangan.util.lha.resources.lha";
Properties property = LhaProperty.createDefaultProperty();
try{
ResourceBundle bundle = ResourceBundle.getBundle( path );
Enumeration enumkey = bundle.getKeys();
while( enumkey.hasMoreElements() ){
String key = (String)enumkey.nextElement();
property.put( key, bundle.getString( key ) );
}
}catch( MissingResourceException exception ){
}
if( property.getProperty( "lha.encoding" ).equals( "ShiftJISAuto" ) ){
try{
String encoding = System.getProperty( "file.encoding" );
if( LhaProperty.isCategoryOfShiftJIS( encoding ) ){
property.put( "lha.encoding", encoding );
}else{
property.put( "lha.encoding", "SJIS" );
}
}catch( SecurityException exception ){
property.put( "lha.encoding", "SJIS" );
}
}
return property;
}
/**
* LHA Library for Java のデフォルトのプロパティを生成する。
* jp/gr/java_conf/dangan/util/lha/resources/ 以下に
* 設定ファイルが無かった場合用。
*
* @return デフォルトのプロパティ
*/
private static final Properties createDefaultProperty(){
Properties property = new Properties();
//------------------------------------------------------------------
// encoding of String
property.put( "lha.encoding", LhaProperty.getSystemEncoding() );
//------------------------------------------------------------------
// package names
property.put( "lha.packages", "jp.gr.java_conf.dangan.util.lha" );
//------------------------------------------------------------------
// encoders
property.put( "lha.lzs.encoder", "LzssOutputStream( PostLzsEncoder( out ), HashAndChainedListSearch, [ HashShort ] )" );
property.put( "lha.lz4.encoder", "out" );
property.put( "lha.lz5.encoder", "LzssOutputStream( PostLz5Encoder( out ), HashAndChainedListSearch )" );
property.put( "lha.lhd.encoder", "out" );
property.put( "lha.lh0.encoder", "out" );
property.put( "lha.lh1.encoder", "LzssOutputStream( PostLh1Encoder( out ), HashAndChainedListSearch )" );
property.put( "lha.lh2.encoder", "LzssOutputStream( PostLh2Encoder( out ), HashAndChainedListSearch )" );
property.put( "lha.lh3.encoder", "LzssOutputStream( PostLh3Encoder( out ), HashAndChainedListSearch )" );
property.put( "lha.lh4.encoder", "LzssOutputStream( PostLh5Encoder( out, -lh4- ), HashAndChainedListSearch )" );
property.put( "lha.lh5.encoder", "LzssOutputStream( PostLh5Encoder( out, -lh5- ), HashAndChainedListSearch )" );
property.put( "lha.lh6.encoder", "LzssOutputStream( PostLh5Encoder( out, -lh6- ), HashAndChainedListSearch )" );
property.put( "lha.lh7.encoder", "LzssOutputStream( PostLh5Encoder( out, -lh7- ), HashAndChainedListSearch )" );
//------------------------------------------------------------------
// decoders
property.put( "lha.lzs.decoder", "LzssInputStream( PreLzsDecoder( in ), length )" );
property.put( "lha.lz4.decoder", "in" );
property.put( "lha.lz5.decoder", "LzssInputStream( PreLz5Decoder( in ), length )" );
property.put( "lha.lhd.decoder", "in" );
property.put( "lha.lh0.decoder", "in" );
property.put( "lha.lh1.decoder", "LzssInputStream( PreLh1Decoder( in ), length )" );
property.put( "lha.lh2.decoder", "LzssInputStream( PreLh2Decoder( in ), length )" );
property.put( "lha.lh3.decoder", "LzssInputStream( PreLh3Decoder( in ), length )" );
property.put( "lha.lh4.decoder", "LzssInputStream( PreLh5Decoder( in, -lh4- ), length )" );
property.put( "lha.lh5.decoder", "LzssInputStream( PreLh5Decoder( in, -lh5- ), length )" );
property.put( "lha.lh6.decoder", "LzssInputStream( PreLh5Decoder( in, -lh6- ), length )" );
property.put( "lha.lh7.decoder", "LzssInputStream( PreLh5Decoder( in, -lh7- ), length )" );
//------------------------------------------------------------------
// header
property.put( "lha.header", "LhaHeader( data, encoding )" );
return property;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// encoding
//------------------------------------------------------------------
// private static final String getSystemEncoding()
// private static final boolean isJapanese( String encoding )
// private static final boolean isCategoryOfShiftJIS( String encoding )
//------------------------------------------------------------------
/**
* System.getProperty( "file.encoding" ) で得られる エンコーディングを返す。
* 得られたエンコーディングが 日本語のエンコーディングで、
* なおかつShiftJIS系列で無い場合は強制的に "SJIS" を使用する。
* セキュリティマネージャが システムプロパティへのアクセスを許さない場合は
* "ISO8859_1" を使用する。
*
* @return System.getProperty( "file.encoding" ) で得られる エンコーディング
*/
private static final String getSystemEncoding(){
String encoding;
try{
encoding = System.getProperty( "file.encoding" );
if( LhaProperty.isJapanese( encoding )
&& !LhaProperty.isCategoryOfShiftJIS( encoding ) ){
return "SJIS";
}else{
return encoding;
}
}catch( SecurityException exception ){
encoding = "ISO8859_1";
}
return encoding;
}
/**
* encoding が日本語のエンコーディングであるかを返す。
*
* @param encoding エンコーディング
*
* @return encoding が日本語のエンコーディングなら true 違えば false
*/
private static final boolean isJapanese( String encoding ){
String[] Coverters = { "Cp930", //Japanese EBCDIC
"Cp939", //Japanese EBCDIC
"Cp942", //SJIS OS/2 日本語, Cp932 のスーパーセット, 0x5C -> '\' (半角バックスラッシュ)
"Cp942C", //SJIS OS/2 日本語, Cp932 のスーパーセット, 0x5C -> '¥' (半角円記号)
"Cp943", //SJIS OS/2 日本語, Cp942 のスーパーセット 新JIS対応, 0x5C -> '\' (半角バックスラッシュ)
"Cp943C", //SJIS OS/2 日本語, Cp942 のスーパーセット 新JIS対応, 0x5C -> '¥' (半角円記号)
"Cp33722", //EUC IBM 日本語,
"MS932", //Windows 日本語
"SJIS", //Shift-JIS、日本語
"EUC_JP", //EUC, 日本語 JIS X 0201, 0208, 0212
"ISO2022JP", //JIS X 0201, ISO 2022 形式の 0208、日本語
"JIS0201", //JIS X 0201, 日本語
"JIS0208", //JIS X 0208, 日本語
"JIS0212", //JIS X 0212, 日本語
"JISAutoDetect" }; //Shift-JIS EUC-JP ISO 2022 JP の検出および変換。読み込み専用。
for( int i = 0 ; i < Coverters.length ; i++ ){
if( encoding.equals( Coverters[i] ) ){
return true;
}
}
String[] Aliases = { "eucjis", "euc-jp", "eucjp", "x-euc-jp", "x-eucjp", //Aliases of "EUC_JP"
"csEUCPkdFmtJapanese", //Alias of "EUCJIS"(?)
"extended_unix_code_packed_format_for_japanese ", //Alias of "EUCJIS"(?)
"shift_jis", "ms_kanji", "csShiftJIS", //JDK1.1.1 - JDK1.1.7B Alias of "SJIS", JDK1.2 - JDK1.3 Alias of "MS932", JDK1.4 Alias of "SJIS"
"csWindows31J", "windows-31j", //Alias of "MS932"
"x-sjis", //JDK1.2 Alias of "MS932", JDK1.3 Alias of "SJIS", JDK1.4 Alias of "MS932"
"jis", //Alias of "ISO2022JP"
"iso-2022-jp", //JDK1.1.1-JDK1.1.5 Alias of "JIS", JDK1.1.6- Alias of "ISO2022JP"
"csISO2022JP", //JDK1.1.1-JDK1.1.5 Alias of "JIS", JDK1.1.6- Alias of "ISO2022JP"
"jis_encoding", //JDK1.1.1-JDK1.1.5 Alias of "JIS", JDK1.1.6- Alias of "ISO2022JP"
"csJISEncoding", //JDK1.1.1-JDK1.1.5 Alias of "JIS", JDK1.1.6- Alias of "ISO2022JP"
"jis auto detect", //Alias of "JISAutoDetect"
"cp930", "ibm-930", "ibm930", "930", //Aliases of "Cp930"
"cp939", "ibm-939", "ibm939", "939", //Aliases of "Cp939"
"cp942", "ibm-942", "ibm942", "942", //Aliases of "Cp942"
"cp942c", //Alias of "Cp942C"
"cp943", "ibm-943", "ibm943", "943", //Aliases of "Cp943"
"cp943c", //Alias of "Cp943C"
"cp33722", "ibm-33722", "ibm33722", "33722" }; //Aliases of "Cp33722"
for( int i = 0 ; i < Aliases.length ; i++ ){
if( encoding.equalsIgnoreCase( Aliases[i] ) ){
return true;
}
}
return false;
}
/**
* encoding が ShiftJIS 系列のエンコーディングであるかを返す。
*
* @param encoding エンコーディング
*
* @return encoding が日本語のエンコーディングなら true 違えば false
*/
private static final boolean isCategoryOfShiftJIS( String encoding ){
String[] Coverters = { "Cp942", //SJIS OS/2 日本語, Cp932 のスーパーセット, 0x5C -> '\' (半角バックスラッシュ)
"Cp942C", //SJIS OS/2 日本語, Cp932 のスーパーセット, 0x5C -> '¥' (半角円記号)
"Cp943", //SJIS OS/2 日本語, Cp942 のスーパーセット 新JIS対応, 0x5C -> '\' (半角バックスラッシュ)
"Cp943C", //SJIS OS/2 日本語, Cp942 のスーパーセット 新JIS対応, 0x5C -> '¥' (半角円記号)
"MS932", //Windows 日本語
"SJIS" }; //Shift-JIS、日本語
for( int i = 0 ; i < Coverters.length ; i++ ){
if( encoding.equals( Coverters[i] ) ){
return true;
}
}
String[] Aliases = { "shift_jis", "ms_kanji", "csShiftJIS", //JDK1.1.1 - JDK1.1.7B Alias of "SJIS", JDK1.2 - JDK1.3 Alias of "MS932", JDK1.4 Alias of "SJIS"
"csWindows31J", "windows-31j", //Alias of "MS932"
"x-sjis", //JDK1.2 Alias of "MS932", JDK1.3 Alias of "SJIS", JDK1.4 Alias of "MS932"
"cp942", "ibm-942", "ibm942", "942", //Aliases of "Cp942"
"cp942c", //Alias of "Cp942C"
"cp943", "ibm-943", "ibm943", "943", //Aliases of "Cp943"
"cp943c" }; //Alias of "Cp943C"
for( int i = 0 ; i < Aliases.length ; i++ ){
if( encoding.equalsIgnoreCase( Aliases[i] ) ){
return true;
}
}
return false;
}
}
//end of LhaProperty.java
jp/gr/java_conf/dangan/util/lha/LhaRetainedOutputStream.java 100644 0 0 131436 7575521012 21601 0 ustar 0 0 //start of LhaRetainedOutputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LhaRetainedOutputStream.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Vector;
import java.util.Properties;
import jp.gr.java_conf.dangan.util.lha.CRC16;
import jp.gr.java_conf.dangan.util.lha.LhaHeader;
import jp.gr.java_conf.dangan.util.lha.LhaProperty;
import jp.gr.java_conf.dangan.util.lha.CompressMethod;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
/**
* 接続されたRandomAccessFileに 圧縮データを出力するためのユーティリティクラス。
* java.util.zip.ZipOutputStream と似たインターフェイスを持つように作った。
* 圧縮失敗時( 圧縮後サイズが圧縮前サイズを上回った場合 )の処理を自動的に行う。
* 進捗報告を実装する場合、このような処理をクラス内に隠蔽すると進捗報告は何秒間か
* 時によっては何十分も応答しなくなる。(例えばギガバイト級のデータを扱った場合)
* このような事態を避けたい場合は LhaImmediateOutputStreamを使用すること。
* また、JDK 1.1 以前では RandomAccessFile が setLength を持たないため、
* 書庫データの後ろに他のデータがある場合でもファイルサイズを切り詰めることが出来ない。
* この問題点は常にサイズ0の新しいファイルを開く事によって回避する事ができる。
*
*
* -- revision history --
* $Log: LhaRetainedOutputStream.java,v $
* Revision 1.2 2002/12/11 02:25:14 dangan
* [bug fix]
* jdk1.2 でコンパイルできなかった箇所を修正。
*
* Revision 1.1 2002/12/08 00:00:00 dangan
* [maintenance]
* LhaConstants から CompressMethod へのクラス名の変更に合わせて修正。
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [change]
* コンストラクタから 引数に String encode を取るものを廃止、
* Properties を引数に取るものを追加。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.2 $
*/
public class LhaRetainedOutputStream extends OutputStream{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private RandomAccessFile archive
//------------------------------------------------------------------
/**
* 書庫ファイル
*/
private RandomAccessFile archive;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// to compress a file
//------------------------------------------------------------------
// private OutputStream out
// private RandomAccessFileOutputStream rafo
// private LhaHeader header
// private String encoding
// private long headerpos
// private CRC16 crc
//------------------------------------------------------------------
/**
* 圧縮用出力ストリーム
*/
private OutputStream out;
/**
* 圧縮用出力ストリーム
*/
private RandomAccessFileOutputStream rafo;
/**
* 現在圧縮中のヘッダ
*/
private LhaHeader header;
/**
* ヘッダの出力に使用したエンコーディング
*/
private String encoding;
/**
* ヘッダ位置
*/
private long headerpos;
/**
* CRC値算出用
*/
private CRC16 crc;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// property
//------------------------------------------------------------------
// private Properties property
//------------------------------------------------------------------
/**
* 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*/
private Properties property;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private LhaRetainedOutputStream()
// public LhaRetainedOutputStream( String filename )
// public LhaRetainedOutputStream( String filename, Properties property )
// public LhaRetainedOutputStream( File file )
// public LhaRetainedOutputStream( File file, Properties property )
// public LhaRetainedOutputStream( RandomAccessFile archive )
// public LhaRetainedOutputStream( RandomAccessFile archive, Properties property )
// private void constructerHelper( RandomAccesFile archive, Properties property )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ
* 使用不可
*/
private LhaRetainedOutputStream(){ }
/**
* filename のファイルに 圧縮データを出力するOutputStreamを構築する。
* 各圧縮形式に対応した符号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param filename 圧縮データを書きこむファイルの名前
*
* @exception FileNotFoundException
* filename で与えられたファイルが見つからない場合。
* @exception SecurityException
* セキュリティマネージャがファイルへのアクセスを許さない場合。
*
* @see LhaProperty#getProperties()
*/
public LhaRetainedOutputStream( String filename )
throws FileNotFoundException {
if( filename != null ){
RandomAccessFile file = new RandomAccessFile( filename, "rw" ); //throws FileNotFoundException, SecurityException
Properties property = LhaProperty.getProperties();
this.constructerHelper( file, property );
}else{
throw new NullPointerException( "filename" );
}
}
/**
* filename のファイルに 圧縮データを出力するOutputStreamを構築する。
*
* @param filename 圧縮データを書きこむファイルの名前
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*
* @exception FileNotFoundException
* filename で与えられたファイルが見つからない場合。
* @exception SecurityException
* セキュリティマネージャがファイルへのアクセスを許さない場合。
*
* @see LhaProperty
*/
public LhaRetainedOutputStream( String filename, Properties property )
throws FileNotFoundException {
if( filename != null ){
RandomAccessFile file = new RandomAccessFile( filename, "rw" ); //throws FileNotFoundException, SecurityException
this.constructerHelper( file, property );
}else{
throw new NullPointerException( "filename" );
}
}
/**
* filename のファイルに 圧縮データを出力するOutputStreamを構築する。
* 各圧縮形式に対応した符号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param filename 圧縮データを書きこむファイルの名前
*
* @exception FileNotFoundException
* filename で与えられたファイルが見つからない場合。
* @exception SecurityException
* セキュリティマネージャがファイルへのアクセスを許さない場合。
* @exception IOException
* JDK1.2 でコンパイルするためだけに存在する。
*
* @see LhaProperty#getProperties()
*/
public LhaRetainedOutputStream( File filename ) throws IOException {
if( filename != null ){
RandomAccessFile file = new RandomAccessFile( filename, "rw" ); //throws FileNotFoundException, SecurityException
Properties property = LhaProperty.getProperties();
this.constructerHelper( file, property );
}else{
throw new NullPointerException( "filename" );
}
}
/**
* filename のファイルに 圧縮データを出力するOutputStreamを構築する。
*
* @param filename 圧縮データを書きこむファイルの名前
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*
* @exception FileNotFoundException
* filename で与えられたファイルが見つからない場合。
* @exception SecurityException
* セキュリティマネージャがファイルへのアクセスを許さない場合。
* @exception IOException
* JDK1.2 でコンパイルするためだけに存在する。
*
* @see LhaProperty
*/
public LhaRetainedOutputStream( File filename, Properties property )
throws IOException {
if( filename != null ){
RandomAccessFile file = new RandomAccessFile( filename, "rw" ); //throws FileNotFoundException, SecurityException
this.constructerHelper( file, property );
}else{
throw new NullPointerException( "filename" );
}
}
/**
* fileに 圧縮データを出力するOutputStreamを構築する。
* 各圧縮形式に対応した符号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param file RandomAccessFile のインスタンス。
*
* - 既に close() されていない事。
*
- コンストラクタの mode には "rw" オプションを使用して、
* 読みこみと書きこみが出来るように生成されたインスタンスであること。
*
* の条件を満たすもの。
*
* @see LhaProperty#getProperties()
*/
public LhaRetainedOutputStream( RandomAccessFile file ){
if( file != null ){
Properties property = LhaProperty.getProperties();
this.constructerHelper( file, property );
}else{
throw new NullPointerException( "out" );
}
}
/**
* fileに 圧縮データを出力するOutputStreamを構築する。
* 各圧縮形式に対応した符号器の生成式等を持つプロパティには
* LhaProperty.getProperties() で得られたプロパティが使用される。
*
* @param file RandomAccessFile のインスタンス。
*
* - 既に close() されていない事。
*
- コンストラクタの mode には "rw" オプションを使用して、
* 読みこみと書きこみが出来るように生成されたインスタンスであること。
*
* の条件を満たすもの。
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*
* @see LhaProperty
*/
public LhaRetainedOutputStream( RandomAccessFile file,
Properties property ){
if( file != null
&& property != null ){
this.constructerHelper( file, property ); //throws UnsupportedEncodingException
}else if( file == null ){
throw new NullPointerException( "null" );
}else{
throw new NullPointerException( "property" );
}
}
/**
* コンストラクタの初期化処理を担当するメソッド。
*
* @param file RandomAccessFile のインスタンス。
*
* - 既に close() されていない事。
*
- コンストラクタの mode には "rw" オプションを使用して、
* 読みこみと書きこみが出来るように生成されたインスタンスであること。
*
* の条件を満たすもの。
* @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
*/
private void constructerHelper( RandomAccessFile file,
Properties property ){
this.archive = file;
this.out = null;
this.header = null;
this.headerpos = -1;
this.crc = new CRC16();
this.property = property;
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* 現在のエントリに1バイトのデータを書きこむ。
*
* @param data 書きこむデータ
*
* @exception IOException 入出力エラーが発生した場合。
*/
public void write( int data ) throws IOException {
if( this.out != null ){
if( this.header != null ){
crc.update( data );
}
this.out.write( data );
}else{
throw new IOException( "no entry" );
}
}
/**
* 現在のエントリに bufferの内容を全て書き出す。
*
* @param buffer 書き出すデータの入ったバイト配列
*
* @exception IOException 入出力エラーが発生した場合。
*/
public void write( byte[] buffer ) throws IOException {
this.write( buffer, 0, buffer.length );
}
/**
* 現在のエントリに bufferの indexから
* lengthバイトのデータを書き出す。
*
* @param buffer 書き出すデータの入ったバイト配列
* @param index buffer内の書き出すべきデータの開始位置
* @param length データのバイト数
*
* @exception IOException 入出力エラーが発生した場合。
*/
public void write( byte[] buffer, int index, int length ) throws IOException {
if( this.out != null ){
if( this.header != null ){
crc.update( buffer, index, length );
}
this.out.write( buffer, index, length );
}else{
throw new IOException( "no entry" );
}
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* 現在書き込み中のエントリのデータを強制的に出力先に書き出す。
* これは PostLzssEncoder, LzssOutputStream の規約どおり
* flush() しなかった場合とは別のデータを出力する。
* (大抵の場合は 単に圧縮率が低下するだけである。)
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PostLzssEncoder#flush()
* @see LzssOutputStream#flush()
*/
public void flush() throws IOException {
if( this.out != null ){
this.out.flush(); //throws IOException
}else{
throw new IOException( "no entry" );
}
}
/**
* 出力先に全てのデータを出力し、ストリームを閉じる。
* また、使用していた全てのリソースを解放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
if( this.out != null ){
this.closeEntry(); //throws IOException
}
//ターミネータを出力
this.archive.write( 0 ); //throws IOException
try{
this.archive.setLength( this.archive.getFilePointer() ); //After Java1.2 throws IOException
}catch( NoSuchMethodError error ){
}
this.archive.close(); //throws IOException
this.archive = null;
this.header = null;
this.crc = null;
this.property = null;
this.rafo = null;
}
//------------------------------------------------------------------
// original method ( on the model of java.util.zip.ZipOutputStream )
//------------------------------------------------------------------
// manipulate entry
//------------------------------------------------------------------
// public void putNextEntry( LhaHeader header )
// public void putNextEntryAlreadyCompressed( LhaHeader header )
// public void putNextEntryNotYetCompressed( LhaHeader header )
// public void closeEntry()
//------------------------------------------------------------------
/**
* 新しいエントリを書き込むようにストリームを設定する。
* このメソッドは 既に圧縮済みのエントリの場合は
* putNextEntryAlreadyCompressed(),
* 未だに圧縮されていない場合は
* putNextEntryNotYetCompressed() を呼び出す。
* 圧縮されているかの判定は、
*
* - header.getCompressedSize()
* - header.getCRC()
*
* のどれか一つでも LhaHeader.UNKNOWN であれば未だに圧縮されていないとする。
* header には正確な OriginalSize が指定されている必要がある。
*
* @param header 書きこむエントリについての情報を持つ
* LhaHeaderのインスタンス。
*
* @exception IOException 入出力エラーが発生した場合
* @exception IllegalArgumentException
* header.getOriginalSize() が LhaHeader.UNKNOWN を返す場合
*/
public void putNextEntry( LhaHeader header ) throws IOException {
if( header.getCompressedSize() == LhaHeader.UNKNOWN
|| header.getCRC() == LhaHeader.UNKNOWN ){
this.putNextEntryNotYetCompressed( header ); //throws IOException
}else{
this.putNextEntryAlreadyCompressed( header ); //throws IOException
}
}
/**
* 既に圧縮済みのエントリを書きこむようにストリームを設定する。
* 圧縮済みデータが正しい事は、呼び出し側が保証する事。
*
* @param header 書きこむエントリについての情報を持つ
* LhaHeaderのインスタンス。
*
* @exception IOException 入出力エラーが発生した場合
* @exception IllegalArgumentException
*
* - header.getOriginalSize() が LhaHeader.UNKNOWN を返す場合
*
- header.getComressedSize() が LhaHeader.UNKNOWN を返す場合
*
- header.getCRC() が LhaHeader.UNKNOWN を返す場合
*
* の何れか。
*/
public void putNextEntryAlreadyCompressed( LhaHeader header )
throws IOException {
if( header.getOriginalSize() != LhaHeader.UNKNOWN
&& header.getCompressedSize() != LhaHeader.UNKNOWN
&& header.getCRC() != LhaHeader.UNKNOWN ){
if( this.out != null ){
this.closeEntry();
}
this.headerpos = this.archive.getFilePointer();
this.encoding = this.property.getProperty( "lha.encoding" );
if( this.encoding == null ){
this.encoding = LhaProperty.getProperty( "lha.encoding" );
}
this.archive.write( header.getBytes( encoding ) ); //throws IOException
this.out = new RandomAccessFileOutputStream( this.archive, header.getCompressedSize() );
}else if( header.getOriginalSize() == LhaHeader.UNKNOWN ){
throw new IllegalArgumentException( "OriginalSize must not \"LhaHeader.UNKNOWN\"." );
}else if( header.getCompressedSize() == LhaHeader.UNKNOWN ){
throw new IllegalArgumentException( "CompressedSize must not \"LhaHeader.UNKNOWN\"." );
}else{
throw new IllegalArgumentException( "CRC must not \"LhaHeader.UNKNOWN\"." );
}
}
/**
* 未だに圧縮されていないエントリを書きこむようにストリームを設定する。
* header には正確な OriginalSize が指定されている必要がある。
* header に CompressedSize, CRCが指定されていても無視される。
*
* @param header 書きこむエントリについての情報を持つ
* LhaHeaderのインスタンス。
*
* @exception IOException 入出力エラーが発生した場合
* @exception IllegalArgumentException
* header.getOriginalSize() が LhaHeader.UNKNOWN を返す場合
*/
public void putNextEntryNotYetCompressed( LhaHeader header )
throws IOException {
if( header.getOriginalSize() != LhaHeader.UNKNOWN ){
if( this.out != null ){
this.closeEntry();
}
this.crc.reset();
this.headerpos = this.archive.getFilePointer();
this.header = (LhaHeader)header.clone();
this.header.setCompressedSize( 0 );
this.header.setCRC( 0 );
this.encoding = this.property.getProperty( "lha.encoding" );
if( this.encoding == null ){
this.encoding = LhaProperty.getProperty( "lha.encoding" );
}
this.archive.write( this.header.getBytes( encoding ) );
this.rafo = new RandomAccessFileOutputStream( this.archive, header.getOriginalSize() );
this.out = CompressMethod.connectEncoder( this.rafo,
header.getCompressMethod(),
this.property );
}else{
throw new IllegalArgumentException( "OriginalSize must not \"LhaHeader.UNKNOWN\"." );
}
}
/**
* 現在出力中のエントリを閉じ、次のエントリが出力可能な状態にする。
* 圧縮に失敗した(圧縮後サイズが圧縮前サイズを上回った)場合、
* 解凍し無圧縮で格納する。エントリのサイズが大きい場合、
* この処理にはかなりの時間がかかる。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void closeEntry() throws IOException {
if( this.header != null ){
this.out.close();
if( !this.rafo.cache.isEmpty() ){
RandomAccessFileInputStream rafi;
InputStream in;
long pos = this.rafo.start;
rafi = new RandomAccessFileInputStream( this.archive, this.rafo );
in = CompressMethod.connectDecoder( rafi,
header.getCompressMethod(),
this.property,
this.header.getOriginalSize() );
byte[] buffer = new byte[8192];
int length;
while( 0 <= ( length = in.read( buffer ) ) ){
rafi.cache( pos + length );
this.archive.seek( pos );
this.archive.write( buffer, 0, length );
pos += length;
}
in.close();
this.header.setCompressMethod( CompressMethod.LH0 );
}
long pos = this.archive.getFilePointer();
long size = ( pos - this.headerpos
- this.header.getBytes( this.encoding ).length );
this.header.setCompressedSize( size );
if( this.header.getCRC() != LhaHeader.NO_CRC ){
this.header.setCRC( (int)this.crc.getValue() );
}
this.archive.seek( this.headerpos );
this.archive.write( this.header.getBytes( this.encoding ) );
this.archive.seek( pos );
}
this.header = null;
this.out = null;
}
//------------------------------------------------------------------
// inner classes
//------------------------------------------------------------------
// private static class RandomAccessFileOutputStream
// private static class RandomAccessFileInputStream
// private static class Cache
//------------------------------------------------------------------
/**
* RandomAccessFile を OutputStreamの インタフェイスに合わせるためのラッパクラス
*/
private static class RandomAccessFileOutputStream extends OutputStream {
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private RandomAccessFile archive
// private GrowthByteBuffer cache
//------------------------------------------------------------------
/**
* 出力先RandomAccessFile
*/
private RandomAccessFile archive;
/**
* 格納限界を超えて書き込もうとした
* 場合のキャッシュ
*/
private Cache cache;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// position
//------------------------------------------------------------------
// private long start
// private long pos
// private long limit
//------------------------------------------------------------------
/**
* 格納開始位置
*/
private long start;
/**
* 現在処理位置
*/
private long pos;
/**
* 格納限界
*/
private long limit;
//------------------------------------------------------------------
// consutructor
//------------------------------------------------------------------
// public RandomAccessFileOutputStream( RandomAccessFile archive,
// long length )
//------------------------------------------------------------------
/**
* RandomAccessFile をラップした OutputStream を構築する。
*
* @param archive 出力先のRandomAccessFile
* @param length 出力限界長
*
* @exception IOException 入出力エラーエラーが発生した場合
*/
public RandomAccessFileOutputStream( RandomAccessFile archive,
long length ) throws IOException {
this.archive = archive;
this.start = this.archive.getFilePointer(); //throws IOException
this.pos = this.start;
this.limit = this.start + length;
this.cache = new Cache();
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* 接続された RandomAccessFile に1バイト書きこむ。
*
* @param data 書きこむ1byteのデータ
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( int data ) throws IOException {
if( this.pos < this.limit && this.cache.isEmpty() ){
this.pos++;
this.archive.write( data ); //throws IOException
}else{
this.cache.add( new byte[]{ (byte)data } );
}
}
/**
* 接続された RandomAccessFile に buffer の内容を全て書きこむ。
*
* @param buffer 書きこむデータの入ったバイト配列
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException コンストラクタに渡された長さを超えて
* 書きこもうとした場合
*/
public void write( byte[] buffer ) throws IOException {
this.write( buffer, 0, buffer.length ); //throws IOException
}
/**
* 接続されたRandomAccessFileにbufferの内容をindexからlengthバイト書きこむ。
*
* @param buffer 書きこむデータの入ったバイト配列
* @param index buffer内の書きこむデータの開始位置
* @param length 書きこむデータ量
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer, int index, int length )
throws IOException {
if( this.pos + length < this.limit && this.cache.isEmpty() ){
this.pos += length;
this.archive.write( buffer, index, length ); //throws IOException
}else{
this.cache.add( buffer, index, length );
}
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void close()
//------------------------------------------------------------------
/**
* このストリームを閉じて使用していたリソースを開放する。
*/
public void close(){
this.archive = null;
}
}
/**
* RandomAccessFile に InputStreamのインターフェイスをかぶせるラッパクラス。
* 圧縮後のサイズが圧縮前のサイズを上回ったときに解凍して
* 無圧縮で格納しなおす処理のために必要。
*/
private static class RandomAccessFileInputStream extends InputStream {
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private RandomAccessFile archive
// private Cache front
// private Cache rear
//------------------------------------------------------------------
/**
* 読み込み元RandomAccessFile
*/
private RandomAccessFile archive;
/**
* 前部キャッシュ
* 書き込みが読み込みを追い越した時のキャッシュ
*/
private Cache front;
/**
* 後部キャッシュ
* 書き込み限界を超えた分のデータのキャッシュ
*/
private Cache rear;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// position
//------------------------------------------------------------------
// private long pos
// private long limit
//------------------------------------------------------------------
/**
* 現在処理位置
*/
private long pos;
/**
* 読み込み限界
*/
private long limit;
//------------------------------------------------------------------
// consutructor
//------------------------------------------------------------------
// public RandomAccessFileInputStream( RandomAccessFile archive,
// RandomAccessFileOutputStream out )
//------------------------------------------------------------------
/**
* RandomAccessFile をラップした InputStream を構築する。
*
* @param archive データを供給する RandomAccessFile
* @param out 直前に圧縮データを受け取っていた RandomAccessFileOutputStream
*/
public RandomAccessFileInputStream( RandomAccessFile archive,
RandomAccessFileOutputStream out ){
this.archive = archive;
this.pos = out.start;
this.limit = out.pos;
this.front = new Cache();
this.rear = out.cache;
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* キャッシュかRandomAccessFileから 1バイトのデータを読み込む。
*
* @return 読み込まれた1バイトのデータ
* 読み込むデータが無ければ -1
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read() throws IOException {
int return_value = this.front.read();
if( return_value < 0 ){
if( this.pos < this.limit ){
this.archive.seek( this.pos++ );
return_value = this.archive.read();
}else{
return_value = this.rear.read();
}
}
return return_value;
}
/**
* キャッシュか RandomAccessFileから bufferを満たすようにデータを読み込む。
*
* @param buffer 読み込まれたデータを格納するバッファ
*
* @return 実際に読み込まれたデータ量
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer ) throws IOException {
return this.read( buffer, 0, buffer.length );
}
/**
* キャッシュか RandomAccessFileから bufferのindexへlengthバイト読み込む。
*
* @param buffer 読み込まれたデータを格納するバッファ
* @param index buffer内の読み込み開始位置
* @param length 読み込むデータ量
*
* @return 実際に読み込まれたデータ量
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer, int index, int length ) throws IOException {
int count = 0;
int ret = this.front.read( buffer, index, length );
if( 0 <= ret ){
count += ret;
}
this.archive.seek( this.pos ); //throws IOException
ret = Math.min( length - count,
Math.max( (int)( this.limit - this.pos ), 0 ) );
this.archive.readFully( buffer, index + count, ret ); //throws IOException
if( 0 <= ret ){
this.pos += ret;
count += ret;
}
ret = this.rear.read( buffer, index + count, length - count );
if( 0 <= ret ){
count += ret;
}
if( 0 < count ){
return count;
}else{
return -1;
}
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void close()
//------------------------------------------------------------------
/**
* このストリームを閉じ
* 使用していたリソースを開放する。
*/
public void close(){
this.front = null;
this.rear = null;
this.archive = null;
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// public void cache( long pos )
//------------------------------------------------------------------
/**
* posまで読み込んでいなければ、
* 現在読み込み位置からposまでのデータを
* 前部キャッシュにデータを追加する。
*
* @param pos archive内の書き出し位置
*/
public void cache( long pos ) throws IOException {
int length = (int)Math.min( this.limit - this.pos,
pos - this.pos );
byte[] buffer = new byte[ length ];
if( 0 < length ){
this.archive.seek( this.pos ); //throws IOException
this.archive.readFully( buffer ); //throws IOException
this.front.add( buffer );
this.pos += length;
}
}
}
/**
* 書き込み限界を超えた書き込みや
* 読み込み位置を超えた書き込みをした場合に
* データをキャッシュするために使用する。
*/
private static class Cache{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private Vector cache
// private byte[] current
// private int position
//------------------------------------------------------------------
/**
* byte[] の Vector
* 各要素は 全て読み込まれたと
* 同時に捨てられる。
*/
private Vector cache;
/**
* 現在読み込み中の要素
*/
private byte[] current;
/**
* currentの現在処理位置
*/
private int position;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public Cache()
//------------------------------------------------------------------
/**
* データの一時退避機構を構築する。
*/
public Cache(){
this.current = null;
this.position = 0;
this.cache = new Vector();
}
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* キャッシュから 1バイトのデータを
* 0〜255にマップして読み込む。
*
* @return 読み込まれた1byteのデータ
* キャッシュが空でデータが無い場合は -1
*/
public int read(){
if( null != this.current ){
int ret = this.current[ this.position++ ] & 0xFF;
if( this.current.length <= this.position ){
if( 0 < this.cache.size() ){
this.current = (byte[])this.cache.firstElement();
this.cache.removeElementAt( 0 );
}else{
this.current = null;
}
this.position = 0;
}
return ret;
}else{
return -1;
}
}
/**
* キャッシュから bufferのindexで始まる場所へlengthバイト読み込む。
*
* @param buffer 読み込んだデータを保持するバッファ
* @param index buffer内の読み込み開始位置
* @param length 読み込むデータ量
*
* @return 実際に読み込まれたデータ量
* キャッシュが空でデータが無い場合は -1
*/
public int read( byte[] buffer, int index, int length ){
int count = 0;
while( null != this.current && count < length ){
int copylen = Math.min( this.current.length - this.position,
length - count );
System.arraycopy( this.current, this.position,
buffer, index + count, copylen );
this.position += copylen;
count += copylen;
if( this.current.length <= this.position ){
if( 0 < this.cache.size() ){
this.current = (byte[])this.cache.firstElement();
this.cache.removeElementAt( 0 );
}else{
this.current = null;
}
this.position = 0;
}
}
if( count == 0 ){
return -1;
}else{
return count;
}
}
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void add( byte[] buffer )
// public void add( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* キャッシュにデータを追加する。
*
* @param buffer データの格納されたバッファ
*/
public void add( byte[] buffer ){
if( this.current == null ){
this.current = buffer;
}else{
this.cache.addElement( buffer );
}
}
/**
* キャッシュにデータを追加する。
*
* @parma buffer データの格納されたバッファ
* @param index buffer内のデータ開始位置
* @param length 格納されているデータの量
*/
public void add( byte[] buffer, int index, int length ){
byte[] buf = new byte[ length ];
System.arraycopy( buffer, index, buf, 0, length );
if( this.current == null ){
this.current = buf;
}else{
this.cache.addElement( buf );
}
}
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public boolean isEmpty()
//------------------------------------------------------------------
/**
* このキャッシュが空かを得る。
*
* @return このキャッシュが空なら true
* 空でなければ false
*/
public boolean isEmpty(){
return this.current == null;
}
}
}
//end of LhaRetainedOutputStream.java
jp/gr/java_conf/dangan/util/lha/LzssInputStream.java 100644 0 0 46643 7574505600 20144 0 ustar 0 0 //start of LzssInputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LzssInputStream.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.InputStream;
import jp.gr.java_conf.dangan.util.lha.PreLzssDecoder;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
/**
* LZSS 圧縮されたデータを解凍しながら供給する入力ストリーム。
*
*
* -- revision history --
* $Log: LzssInputStream.java,v $
* Revision 1.1 2002/12/08 00:00:00 dangan
* [bug fix]
* mark() 内で接続された PreLzssDecoder の
* mark に与える readLimit の計算が甘かったのを修正。
*
* Revision 1.0 2002/07/25 00:00:00 dangan
* add to version control
* [bug fix]
* available() のスペルミスを修正。
* skip() において decode() を呼ぶ判定条件が間違っていたのを修正。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class LzssInputStream extends InputStream{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private PreLzssDecoder decoder
//------------------------------------------------------------------
/**
* LZSS圧縮コードを返す入力ストリーム
*/
private PreLzssDecoder decoder;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private int Threshold
// private int MaxMatch
// private long Length
//------------------------------------------------------------------
/**
* LZSS圧縮に使用される閾値。
* 一致長が この値以上であれば、圧縮コードを出力する。
*/
private int Threshold;
/**
* LZSS圧縮に使用される値。
* 最大一致長を示す。
*/
private int MaxMatch;
/**
* 解凍後のデータサイズ
*/
private long Length;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// text buffer
//------------------------------------------------------------------
// private byte[] TextBuffer
// private long TextPosition
// private long TextDecoded
//------------------------------------------------------------------
/**
* LZSS圧縮を展開するためのバッファ。
*/
private byte[] TextBuffer;
/**
* 現在読み込み位置。
* read() によって外部に読み出された位置を示す。
*/
private long TextPosition;
/**
* 現在読み込み位置。
* LZSS圧縮コードを展開して得られた位置を示す。
*/
private long TextDecoded;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// backup for mark/reset
//------------------------------------------------------------------
// private byte[] MarkTextBuffer
// private long MarkTextPosition
// private long MarkTextDecoded
//------------------------------------------------------------------
/** TextBuffer のバックアップ用 */
private byte[] MarkTextBuffer;
/** TextPosition のバックアップ用 */
private long MarkTextPosition;
/** TextDecoded のバックアップ用 */
private long MarkTextDecoded;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private LzssInputStream()
// public LzssInputStream( PreLzssDecoder decoder )
// public LzssInputStream( PreLzssDecoder decoder, long length )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private LzssInputStream(){ }
/**
* in から LZSS圧縮データ の入力を受けて、
* 解凍されたデータを提供する入力ストリームを構築する。
* このコンストラクタから生成された LzssInputStreamは
* -lh1-等の解凍データの最後のデータを読み込んだ後、
* 次のデータの読み取りで必ずEndOfStreamに達するとは
* 限らないデータを正常に復元できない(終端以降にゴミ
* データがつく可能性がある)。
*
* @param decoder LZSS圧縮データ供給ストリーム
*/
public LzssInputStream( PreLzssDecoder decoder ){
this( decoder, Long.MAX_VALUE );
}
/**
* in から LZSS圧縮データ の入力を受けて、
* 解凍されたデータを提供する入力ストリームを構築する。
*
*
* @param decoder LZSS圧縮データ供給ストリーム
* @param length 解凍後のサイズ
*/
public LzssInputStream( PreLzssDecoder decoder,
long length ){
this.MaxMatch = decoder.getMaxMatch();
this.Threshold = decoder.getThreshold();
this.Length = length;
this.decoder = decoder;
this.TextBuffer = new byte[ decoder.getDictionarySize() ];
this.TextPosition = 0;
this.TextDecoded = 0;
if( this.decoder instanceof PreLz5Decoder )
this.initLz5TextBuffer();
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int read()
// public int read( byte[] buffer )
// public int read( byte[] buffer, int index, int length )
// public long skip( long length )
//------------------------------------------------------------------
/**
* コンストラクタで指定された PreLzssDecoder の
* 圧縮されたデータを解凍し、1バイトのデータを供給する。
*
* @return 解凍された 1バイトのデータ
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read() throws IOException {
if( this.TextDecoded <= this.TextPosition ){
try{
this.decode(); //throws EOFException IOException
}catch( EOFException exception ){
if( this.TextDecoded <= this.TextPosition )
return -1;
}
}
return this.TextBuffer[ (int)this.TextPosition++
& ( this.TextBuffer.length - 1 ) ] & 0xFF;
}
/**
* コンストラクタで指定された PreLzssDecoder の
* 圧縮されたデータを解凍し、bufferを満たすように
* 解凍されたデータを読み込む。
*
* @param buffer データを読み込むバッファ
*
* @return 読みこんだデータ量
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer ) throws IOException {
return this.read( buffer, 0, buffer.length );
}
/**
* コンストラクタで指定された PreLzssDecoder の
* 圧縮されたデータを解凍し、buffer の index から
* length バイトのデータを読み込む。
*
* @param buffer データを読み込むバッファ
* @param index buffer 内のデータ読みこみ開始位置
* @param length 読み込むデータ量
*
* @return 読みこんだデータ量
*
* @exception IOException 入出力エラーが発生した場合
*/
public int read( byte[] buffer, int index, int length ) throws IOException {
int position = index;
int end = index + length;
try{
while( position < end ){
if( this.TextDecoded <= this.TextPosition )
this.decode(); //throws IOException
position = this.copyTextBufferToBuffer( buffer, position, end );
}
}catch( EOFException exception ){
position = this.copyTextBufferToBuffer( buffer, position, end );
if( position == index ) return -1;
}
return position - index;
}
/**
* 解凍されたデータを lengthバイト読み飛ばす。
*
* @param length 読み飛ばすデータ量(単位はバイト)
*
* @return 実際に読み飛ばしたバイト数
*
* @exception IOException 入出力エラーが発生した場合
*/
public long skip( long length ) throws IOException {
long end = this.TextPosition + length;
try{
while( this.TextPosition < end ){
if( this.TextDecoded <= this.TextPosition )
this.decode();
this.TextPosition = Math.min( end, this.TextDecoded );
}
}catch( EOFException exception ){
this.TextPosition = Math.min( end, this.TextDecoded );
}
return length - ( end - this.TextPosition );
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupported()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
* InputStream の mark() と違い、 readLimit で設定した
* 限界バイト数より前にマーク位置が無効になる可能性がある。
* ただし、readLimit を無視して無限に reset() 可能な
* InputStream と接続している場合は readLimit に
* どのような値を設定されても
* reset() で必ずマーク位置に復旧できる事を保証する。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*
* @see PreLzssDecoder#mark(int)
*/
public void mark( int readLimit ){
readLimit -= (int)( this.TextDecoded - this.TextPosition );
int Size = this.TextBuffer.length - this.MaxMatch;
readLimit = ( readLimit + Size - 1 ) / Size * Size;
this.decoder.mark( Math.max( readLimit, 0 ) );
if( this.MarkTextBuffer == null ){
this.MarkTextBuffer = (byte[])this.TextBuffer.clone();
}else{
System.arraycopy( this.TextBuffer, 0,
this.MarkTextBuffer, 0,
this.TextBuffer.length );
}
this.MarkTextPosition = this.TextPosition;
this.MarkTextDecoded = this.TextDecoded;
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void reset() throws IOException {
if( this.MarkTextBuffer == null ){
throw new IOException( "not marked." );
}else if( this.TextDecoded - this.MarkTextPosition
<= this.TextBuffer.length ){
this.TextPosition = this.MarkTextPosition;
}else if( this.decoder.markSupported() ){
//reset
this.decoder.reset(); //throws IOException
System.arraycopy( this.MarkTextBuffer, 0,
this.TextBuffer, 0,
this.TextBuffer.length );
this.TextPosition = this.MarkTextPosition;
this.TextDecoded = this.MarkTextDecoded;
}else{
throw new IOException( "mark/reset not supported." );
}
}
/**
* 接続された入力ストリームが mark() と reset() を
* サポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public boolean markSupported(){
return this.decoder.markSupported();
}
//------------------------------------------------------------------
// method of java.io.InputStream
//------------------------------------------------------------------
// other methods
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* 接続された入力ストリームからブロックしないで
* 読み込むことのできるバイト数を得る。
*
* @return ブロックしないで読み出せるバイト数。
*
* @exception IOException 入出力エラーが発生した場合
*/
public int available() throws IOException {
return (int)( this.TextDecoded - this.TextPosition )
+ this.decoder.available();
}
/**
* この入力ストリームを閉じ、使用していた
* 全てのリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.decoder.close();
this.decoder = null;
this.TextBuffer = null;
this.MarkTextBuffer = null;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private void decode()
// private int copyTextBufferToBuffer( byte[] buffer, int position, int end )
// private void initLz5TextBuffer()
//------------------------------------------------------------------
/**
* private変数 this.in から圧縮データを読み込み
* 解凍しながら TextBuffer にデータを書きこむ。
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException ストリーム終端に達した場合
*/
private void decode() throws IOException {
if( this.TextDecoded < this.Length ){
final int TextMask = this.TextBuffer.length - 1;
final int TextStart = (int)this.TextDecoded & TextMask;
int TextPos = TextStart;
int TextLimit = (int)( Math.min( this.TextPosition
+ this.TextBuffer.length
- this.MaxMatch,
this.Length )
- this.TextDecoded ) + TextStart;
try{
while( TextPos < TextLimit ){
int Code = this.decoder.readCode(); //throws EOFException IOException
if( Code < 0x100 ){
this.TextBuffer[ TextMask & TextPos++ ] = (byte)Code;
}else{
int MatchLength = ( Code & 0xFF ) + this.Threshold;
int MatchPosition = TextPos - this.decoder.readOffset() - 1;//throws IOException
while( 0 < MatchLength-- )
this.TextBuffer[ TextMask & TextPos++ ]
= this.TextBuffer[ TextMask & MatchPosition++ ];
}
}
}finally{
this.TextDecoded += TextPos - TextStart;
}
}else{
throw new EOFException();
}
}
/**
* private 変数 this.TextBuffer から bufferにデータを転送する。
*
* @param buffer TextBufferの内容をコピーするバッファ
* @param position buffer内の書き込み現在位置
* @param end buffer内の書き込み終了位置
*
* @return bufferの次に書き込みが行われるべき位置
*/
private int copyTextBufferToBuffer( byte[] buffer, int position, int end ){
if( ( this.TextPosition & ~( this.TextBuffer.length - 1 ) )
< ( this.TextDecoded & ~( this.TextBuffer.length - 1 ) ) ){
int length = Math.min( this.TextBuffer.length -
( (int)this.TextPosition
& this.TextBuffer.length - 1 ),
end - position );
System.arraycopy( this.TextBuffer,
(int)this.TextPosition
& this.TextBuffer.length - 1,
buffer, position, length );
this.TextPosition += length;
position += length;
}
if( this.TextPosition < this.TextDecoded ){
int length = Math.min( (int)( this.TextDecoded
- this.TextPosition ),
end - position );
System.arraycopy( this.TextBuffer,
(int)this.TextPosition
& this.TextBuffer.length - 1,
buffer, position, length );
this.TextPosition += length;
position += length;
}
return position;
}
/**
* -lz5- 用に TextBuffer を初期化する。
*/
private void initLz5TextBuffer(){
int position = 18;
for( int i = 0 ; i < 256 ; i++ )
for( int j = 0 ; j < 13 ; j++ )
this.TextBuffer[ position++ ] = (byte)i;
for( int i = 0 ; i < 256 ; i++ )
this.TextBuffer[ position++ ] = (byte)i;
for( int i = 0 ; i < 256 ; i++ )
this.TextBuffer[ position++ ] = (byte)(255 - i);
for( int i = 0 ; i < 128 ; i++ )
this.TextBuffer[ position++ ] = 0;
while( position < this.TextBuffer.length )
this.TextBuffer[ position++ ] = (byte)' ';
}
}
//end of LzssInputStream.java
jp/gr/java_conf/dangan/util/lha/LzssOutputStream.java 100644 0 0 70617 7573764200 20345 0 ustar 0 0 //start of LzssOutputStream.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LzssOutputStream.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.OutputStream;
import jp.gr.java_conf.dangan.lang.reflect.Factory;
import jp.gr.java_conf.dangan.util.lha.PostLzssEncoder;
import jp.gr.java_conf.dangan.util.lha.LzssSearchMethod;
import jp.gr.java_conf.dangan.util.lha.HashAndChainedListSearch;
//import exceptions
import java.io.IOException;
import java.lang.NoSuchMethodException;
import java.lang.ClassNotFoundException;
import java.lang.InstantiationException;
import java.lang.reflect.InvocationTargetException;
import java.lang.Error;
import java.lang.NoSuchMethodError;
import java.lang.InstantiationError;
import java.lang.NoClassDefFoundError;
/**
* データを LZSS圧縮しながら
* 指定された PostLzssEncoder に出力する圧縮用出力ストリーム。
*
*
* -- revision history --
* $Log: LzssOutputStream.java,v $
* Revision 1.2 2002/12/06 00:00:00 dangan
* [change]
* flush() で write() された全てのデータを
* 接続された PostLzssEncoder に出力するように修正。
* [maintenance]
* slide幅が常に DictionarySize バイトになるように修正。
*
* Revision 1.1 2002/10/20 00:00:00 dangan
* [bug fix]
* 初期状態で flush() したり 連続で flush() すると
* ( lastsearchret が NEEDSEARCH の時に encode() が呼ばれると )
* 直後の 1バイトが化けていた。
* flush() 時に putLength() を考慮していなかったため
* 検索機構を破壊するような searchAndPut を行っていたのを修正。
* flush() 時に TextBuffer 最後尾のMaxMatchバイトのデータを出力していなかった。
*
* Revision 1.0 2002/07/25 00:00:00 dangan
* add to version control
* [bug fix]
* getMatchLen() で searchret >> 22 とすべきところが
* searchret >>> 22 となっていたのを修正。
* [maintenance]
* LhaUtil.createInstance() の使用をやめ
* 代わりに Factory.createInstance() を使用する。
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.2 $
*/
public class LzssOutputStream extends OutputStream{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// special value
//------------------------------------------------------------------
// private static final int NEEDSEARCH
// private static final int NOMATCH
//------------------------------------------------------------------
/**
* lastsearchret に登録する値。
* searchAndPutの処理が必要である事を示す。
*/
private static final int NEEDSEARCH = 0;
/**
* searchret がこの値だった場合、
* 検索の結果、閾値以上の一致が見つからなかった事を示す。
*/
public static final int NOMATCH = -1;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private PostLzssEncoder encoder
//------------------------------------------------------------------
/**
* LZSS圧縮コードを排出する先の出力ストリーム
*/
private PostLzssEncoder encoder;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private int DictionarySize
// private int Threshold
// private int MaxMatch
//------------------------------------------------------------------
/**
* LZSS辞書サイズ。
*/
private int DictionarySize;
/**
* LZSS圧縮に使用される閾値。
* 一致長が この値以上であれば、圧縮コードを出力する。
*/
private int Threshold;
/**
* LZSS圧縮に使用される値。
* 最大一致長を示す。
*/
private int MaxMatch;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// text buffer
//------------------------------------------------------------------
// private byte[] TextBuffer
// private int DictionaryLimit
// private int writtenPos
// private int putPos
// private int searchedPos
//------------------------------------------------------------------
/**
* LZSS圧縮を施すためのバッファ。
* 前半は辞書領域、
* 後半は圧縮を施すためのデータの入ったバッファ。
*/
private byte[] TextBuffer;
/**
* 辞書の限界位置。
* TextBuffer前半の辞書領域にデータが無い場合に
* 辞書領域にある不定のデータ(Javaでは0)を使用
* して圧縮が行われるのを抑止する。
*/
private int DictionaryLimit;
/**
* TextBuffer内書き込み完了位置
* LzssOutputStream.write() によって書き込まれた位置
*
* 以下の3者の関係は putPos <= searchedPos <= writtenPos となる。
*/
private int writtenPos;
/**
* TextBuffer内 put() 完了位置
* LzssSearchMethod の put() もしくは searchAndPut() で
* 検索機構への登録が完了した位置
*/
private int putPos;
/**
* TextBuffer内 現在検索位置
* 次に LzssSearchMethod の search() もしくは searchAndPut() で
* 検索をすべき位置
*/
private int searchPos;
/**
* 前回のencodeの最後のsearchretを保存しておく
* コンストラクタでは lastsearchret に無効な
* 数字である事を示す LzssOutputStream.NEEDSEARCHを
* 入力しておく。
*/
private int lastsearchret;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// search method
//------------------------------------------------------------------
// private LzssSearchMethod method
//------------------------------------------------------------------
/**
* 検索をつかさどるクラス
*/
private LzssSearchMethod method;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private LzssOutputStream()
// public LzssOutputStream( PostLzssEncoder encoder )
// public LzssOutputStream( PostLzssEncoder encoder, String SearchMethod )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private LzssOutputStream(){ }
/**
* write() によって書きこまれたデータを
* LZSSで圧縮し、圧縮したデータを encoderに出力する
* 出力ストリームを構築する。
*
* @param encoder LZSS圧縮データ出力ストリーム
*/
public LzssOutputStream( PostLzssEncoder encoder ){
this( encoder,
HashAndChainedListSearch.class.getName(),
new Object[0] );
}
/**
* write() によって書きこまれたデータを
* LZSSで圧縮し、圧縮したデータを encoderに出力する
* 出力ストリームを構築する。
*
* @param encoder LZSS圧縮データ出力ストリーム
* @param LzssSearchMethodClassName
* LzssSearchMethod の実装を示すパッケージ名も含めたクラス名
*
* @exception NoClassDefFoundError
* LzssSearchMethodClassName で与えられたクラスが
* 見つからない場合。
* @exception InstantiationError
* LzssSearchMethodClassName で与えられたクラスが
* abstract class であるためインスタンスを生成できない場合。
* @exception NoSuchMethodError
* LzssSearchMethodClassName で与えられたクラスが
* コンストラクタ LzssSearchMethod( int, int, int, byte[], int )
* を持たない場合
*/
public LzssOutputStream( PostLzssEncoder encoder,
String LzssSearchMethodClassName ){
this( encoder,
LzssSearchMethodClassName,
new Object[0] );
}
/**
* write() によって書きこまれたデータを
* LZSSで圧縮し、圧縮したデータを encoderに出力する
* 出力ストリームを構築する。
*
* @param encoder LZSS圧縮データ出力ストリーム
* @param LzssSearchMethodClassName
* LzssSearchMethod の実装を示すパッケージ名も含めたクラス名
*
* @exception NoClassDefFoundError
* LzssSearchMethodClassName で与えられたクラスが
* 見つからない場合。
* @exception InstantiationError
* LzssSearchMethodClassName で与えられたクラスが
* abstract class であるためインスタンスを生成できない場合。
* @exception NoSuchMethodError
* LzssSearchMethodClassName で与えられたクラスが
* コンストラクタ LzssSearchMethod( int, int, int, byte[] )
* を持たない場合
*/
public LzssOutputStream( PostLzssEncoder encoder,
String LzssSearchMethodClassName,
Object[] LzssSearchMethodExtraArguments ){
this.DictionarySize = encoder.getDictionarySize();
this.MaxMatch = encoder.getMaxMatch();
this.Threshold = encoder.getThreshold();
this.encoder = encoder;
this.TextBuffer = new byte[ this.DictionarySize * 2
+ this.MaxMatch ];
this.writtenPos = this.DictionarySize;
this.putPos = this.DictionarySize;
this.searchPos = this.DictionarySize;
this.DictionaryLimit = this.DictionarySize;
this.lastsearchret = LzssOutputStream.NEEDSEARCH;
Object[] arguments = new Object[ LzssSearchMethodExtraArguments.length + 4 ];
arguments[0] = new Integer( this.DictionarySize );
arguments[1] = new Integer( this.MaxMatch );
arguments[2] = new Integer( this.Threshold );
arguments[3] = this.TextBuffer;
for( int i = 0 ; i < LzssSearchMethodExtraArguments.length ; i++ ){
arguments[4+i] = LzssSearchMethodExtraArguments[i];
}
try{
this.method = (LzssSearchMethod)Factory.createInstance(
LzssSearchMethodClassName,
arguments ); //throw ClasNotfoundException, InvocationTargetException, NoSuchMethodException, InstantiationException
}catch( ClassNotFoundException exception ){
throw new NoClassDefFoundError( exception.getMessage() );
}catch( InvocationTargetException exception ){
throw new Error( exception.getTargetException().getMessage() );
}catch( NoSuchMethodException exception ){
throw new NoSuchMethodError( exception.getMessage() );
}catch( InstantiationException exception ){
throw new InstantiationError( exception.getMessage() );
}
}
//------------------------------------------------------------------
// method of java.io.OutputStream method
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void write( int data )
// public void write( byte[] buffer )
// public void write( byte[] buffer, int index, int length )
//------------------------------------------------------------------
/**
* 圧縮機構に1バイトのデータを出力する。
* 実際にPostLzssEncoder にデータが渡されるのは
* TextBuffer が満たされたときか、
* flush で明示的に出力を指示した時のみ。
*
* @param data 1バイトのデータ
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( int data ) throws IOException {
this.TextBuffer[ this.writtenPos++ ] = (byte)data;
if( this.TextBuffer.length <= this.writtenPos ){
this.encode( false ); //throws IOException
this.slide();
}
}
/**
* 圧縮機構に buffer 内のデータを全て出力する。
* 実際にPostLzssEncoder にデータが渡されるのは
* TextBuffer が満たされたときか、
* flush で明示的に出力を指示した時のみ。
*
* @param buffer データの格納されたバッファ
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer ) throws IOException {
this.write( buffer, 0, buffer.length ); //throws IOException
}
/**
* 圧縮機構に buffer 内の index から lengthバイトのデータを出力する。
* 実際にPostLzssEncoder にデータが渡されるのは
* TextBuffer が満たされたときか、
* flush で明示的に出力を指示した時のみ。
*
* @param buffer データの格納されたバッファ
* @param index buffer内データ開始位置
* @param length buffer内データの長さ
*
* @exception IOException 入出力エラーが発生した場合
*/
public void write( byte[] buffer, int index, int length ) throws IOException {
int pos = index;
int end = index + length;
while( pos < end ){
int space = TextBuffer.length - writtenPos;
if( end - pos < space ){
System.arraycopy( buffer, pos,
this.TextBuffer, this.writtenPos,
end - pos );
this.writtenPos += end - pos;
pos = end;
}else{
System.arraycopy( buffer, pos,
this.TextBuffer, this.writtenPos,
space );
this.writtenPos += space;
pos += space;
this.encode( false ); //throws IOException
this.slide();
}
}
}
//------------------------------------------------------------------
// method of java.io.OutputStream
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* 圧縮機構に書き込まれた全てのデータを
* 接続された PostLzssEncoder に出力し、
* 接続された PostLzssEncoder を flush() する。
* このとき、出力するデータの終端付近では
* 検索に search() を使用するため圧縮速度が低下する。
* また flush() しない場合と比べて圧縮率が変化する。
* これは flush() した位置付近ではデータパタンの検索に
* MaxMatch に満たないデータパタンを使用するため、
* 検索結果が不完全になるため。
* この圧縮率の変化は、多くの場合圧縮率が少々低下するだけであるが、
* 例えば次のようなコードは LZ 圧縮を全く行わない。
*
* public void wrongCompress( InputStream in, LzssOutputSteam out ){
* int r;
* while( 0 <= r = in.read() ){
* out.write( r );
* out.flush();
* }
* }
*
* また、このメソッドは PostLzssEncoder.flush() を呼び出すため
* flush() しない場合と比べて、出力データが変化する可能性がある。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PostLzssEncoder#flush()
*/
public void flush() throws IOException {
this.encode( false ); //throw IOException
if( this.DictionarySize * 2 <= this.putPos ){
this.slide();
if( this.searchPos < this.writtenPos ){
this.encode( false ); //throw IOException
}
}
this.encoder.flush(); //throw IOException
}
/**
* このクラスに貯えられた全てのデータを接続された
* PostLzssEncoder に出力し この出力ストリームと、
* 接続されたストリームを閉じ、
* 使用していたリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
while( this.DictionarySize <= this.writtenPos ){
this.encode( true ); //throw IOException
if( this.writtenPos <= this.searchPos ){
break;
}else{
this.slide();
}
}
this.encoder.close(); //throw IOException
this.encoder = null;
this.TextBuffer = null;
this.method = null;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private int encode()
// private void slide( int position )
//------------------------------------------------------------------
/**
* TextBuffer に貯えられたデータを圧縮しながら
* private変数 this.encoder に出力する。
*
* @return TextBuffer 内の出力完了したデータの終端位置 + 1
*
* @exception IOException 入出力エラーが発生した場合
*/
private void encode( boolean last ) throws IOException {
int end = Math.min( this.TextBuffer.length - this.MaxMatch,
this.writtenPos - ( last ? 0 : this.method.putRequires() ) );
if( this.searchPos < end ){
//------------------------------------------------------------------
// 前処理
if( this.lastsearchret == LzssOutputStream.NEEDSEARCH ){
//------------------------------------------------------------------
// 検索機構に未登録のデータパタンを登録
while( this.putPos < this.searchPos - 1 ){
this.method.put( ++this.putPos );
//直前の flush() で put() できなかった
//データパタンを put() しただけの場合は return
if( this.DictionarySize * 2 <= this.putPos ){
return;
}
}
// lastsearchret が NEEDSEARCH なので searchAndPut で検索する。
this.lastsearchret = this.method.searchAndPut( this.searchPos );
}
int searchret = this.lastsearchret;
int matchlen = LzssOutputStream.getMatchLen( searchret );
int matchpos = LzssOutputStream.getMatchPos( searchret );
if( this.writtenPos - this.searchPos < matchlen ){
matchlen = this.writtenPos - this.searchPos;
}
//------------------------------------------------------------------
// メインループ
while( true ){
int lastmatchlen = matchlen;
int lastmatchoff = this.searchPos - matchpos - 1;
searchret = this.method.searchAndPut( ++this.searchPos );
matchlen = LzssOutputStream.getMatchLen( searchret );
matchpos = LzssOutputStream.getMatchPos( searchret );
if( this.writtenPos - this.searchPos < matchlen ){
matchlen = this.writtenPos - this.searchPos;
}
if( lastmatchlen < matchlen || lastmatchlen < this.Threshold ){
this.encoder.writeCode( 0xFF & this.TextBuffer[ this.searchPos - 1 ] ); //throws IOException
if( end <= this.searchPos ){
this.putPos = this.searchPos;
this.lastsearchret = searchret;
break;
}
}else{
this.encoder.writeCode( 256 + lastmatchlen - this.Threshold );//throws IOException
this.encoder.writeOffset( lastmatchoff ); //throws IOException
lastmatchlen--;
if( this.searchPos + lastmatchlen < end ){
while( 0 < --lastmatchlen ){
this.method.put( ++this.searchPos );
}
searchret = this.method.searchAndPut( ++this.searchPos );
matchlen = LzssOutputStream.getMatchLen( searchret );
matchpos = LzssOutputStream.getMatchPos( searchret );
if( this.writtenPos - this.searchPos < matchlen ){
matchlen = this.writtenPos - this.searchPos;
}
}else if( end < this.searchPos + lastmatchlen ){
this.putPos = this.searchPos;
while( this.putPos < end ){
this.method.put( ++this.putPos );
}
this.searchPos += lastmatchlen;
this.lastsearchret = LzssOutputStream.NEEDSEARCH;
break;
}else{
this.putPos = this.searchPos;
while( this.putPos < end - 1 ){
this.method.put( ++this.putPos );
}
this.putPos++;
this.searchPos += lastmatchlen;
this.lastsearchret = this.method.searchAndPut( this.searchPos );
break;
}
}// if( lastmatchlen < matchlen || lastmatchlen < this.Threshold )
}// while( true )
}// if( this.searchPos < end )
//------------------------------------------------------------------
// flush() 専用
// putPos はそのままで searchPos のみ進める。
end = Math.min( this.TextBuffer.length - this.MaxMatch,
this.writtenPos );
if( !last && this.searchPos < end ){
if( this.lastsearchret == LzssOutputStream.NEEDSEARCH ){
this.lastsearchret = this.method.search( this.searchPos, this.putPos );
}
int searchret = this.lastsearchret;
int matchlen = LzssOutputStream.getMatchLen( searchret );
int matchpos = LzssOutputStream.getMatchPos( searchret );
if( this.writtenPos - this.searchPos < matchlen ){
matchlen = this.writtenPos - this.searchPos;
}
while( this.searchPos < end ){
int lastmatchlen = matchlen;
int lastmatchoff = this.searchPos - matchpos - 1;
searchret = this.method.search( ++this.searchPos, this.putPos );
matchlen = LzssOutputStream.getMatchLen( searchret );
matchpos = LzssOutputStream.getMatchPos( searchret );
if( this.writtenPos - this.searchPos < matchlen ){
matchlen = this.writtenPos - this.searchPos;
}
if( lastmatchlen < matchlen || lastmatchlen < this.Threshold ){
this.encoder.writeCode( 0xFF & this.TextBuffer[this.searchPos - 1] ); //throws IOException
}else{
this.encoder.writeCode( 256 + lastmatchlen - this.Threshold ); //throws IOException
this.encoder.writeOffset( lastmatchoff ); //throws IOException
this.searchPos += lastmatchlen - 1;
searchret = this.method.search( this.searchPos, this.putPos );
matchlen = LzssOutputStream.getMatchLen( searchret );
matchpos = LzssOutputStream.getMatchPos( searchret );
if( this.writtenPos - this.searchPos < matchlen ){
matchlen = this.writtenPos - this.searchPos;
}
}
}
this.lastsearchret = LzssOutputStream.NEEDSEARCH;
}
}
/**
* TextBuffer内のpositionまでのデータを
* 前方へ移動する
*
* @param position 次に TextBuffer内で
* DictionarySize の位置に来るべき
* 要素が現在あるindex
*/
private void slide(){
this.DictionaryLimit = Math.max( 0, this.DictionaryLimit - this.DictionarySize );
this.method.slide();
if( this.lastsearchret != LzssOutputStream.NEEDSEARCH ){
int matchlen = LzssOutputStream.getMatchLen( this.lastsearchret );
int matchpos = LzssOutputStream.getMatchPos( this.lastsearchret );
this.lastsearchret = LzssOutputStream.createSearchReturn(
matchlen, matchpos - this.DictionarySize );
}
this.writtenPos -= this.DictionarySize;
this.searchPos -= this.DictionarySize;
this.putPos -= this.DictionarySize;
for( int i = this.DictionaryLimit ; i < this.writtenPos ; i++ )
this.TextBuffer[ i ] = this.TextBuffer[ i + this.DictionarySize ];
}
//------------------------------------------------------------------
// shared methods
//------------------------------------------------------------------
// private static final int createSearchReturn( int matchlen, int matchpos )
// private static final int getMatchLen( int searchret )
// private static final int getMatchPos( int searchret )
//------------------------------------------------------------------
/**
* search の戻り値を生成する。
* search は一致位置を返すが、一致長も同時に返したほうが
* 非常に便利であるため、一致位置も一致長も必要なビット数が
* 少ないことを利用して int型でやり取りする。
* そのための統一した処理を約束する関数。
* この関数で生成された値から 一致位置や一致長を取り出す際には
* getMatchLen、 getMatchPos を使用する。
*
* @param matchlen 一致長
* @param matchpos 一致位置
*
* @return 一致長と一致位置の情報を含むsearchの戻り値
*/
public static final int createSearchReturn( int matchlen, int matchpos ){
return matchlen << 22 | matchpos;
}
/**
* createSearchReturn で生成された searchの戻り値から
* 一致長を取り出す。
*
* @param searchret search の戻り値
*
* @return 一致長
*/
public static final int getMatchLen( int searchret ){
return searchret >> 22;
}
/**
* createSearchReturn で生成された searchの戻り値から
* 一致位置を取り出す。
*
* @param searchret search の戻り値
*
* @return 一致位置
*/
public static final int getMatchPos( int searchret ){
if( 0 <= searchret ) return searchret & 0x3FFFFF;
else return -1;
}
}
//end of LzssOutputStream.java
jp/gr/java_conf/dangan/util/lha/LzssSearchMethod.java 100644 0 0 13761 7573242600 20227 0 ustar 0 0 //start of LzssSearchMethod.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* LzssSearchMethod.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
//import exceptions
/**
* LzssOutputStream で使用される
* 最長一致検索を提供するインターフェイス。
*
*
* コンストラクタの形式は
*
* LzssSearchMethod( int DictionarySize,
* int MaxMatch,
* int Threshold,
* byte[] TextBuffer )
*
* パラメータ:
* DictionarySize - LZSSの辞書サイズ
* MaxMatch - LZSSの最大一致長
* Threshold - LZSSの圧縮/非圧縮の閾値
* TextBuffer - LZSS圧縮を施すデータの入ったバッファ
*
* のような形式に則ること。
* また、追加の引数をとりたい場合は
*
* LzssSearchMethod( int DictionarySize,
* int MaxMatch,
* int Threshold,
* byte[] TextBuffer,
* Object ExtraArgument1,
* Object ExtraArgument2 )
*
* のような形式を用いる。
* なお、コンストラクタの引数チェックは追加の引数がある場合について行えばよい。
*
*
*
* -- revision history --
* $Log: LzssSearchMethod.java,v $
* Revision 1.1 2002/12/04 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [change]
* slide() で引数を取らずに
* スライド幅を常に DictionarySize とするように変更。
* putLength を putRequires に変更
* [maintenance]
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public interface LzssSearchMethod{
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// public abstract void put( int position )
// public abstract int searchAndPut( int position )
// public abstract int search( int position, int lastPutPos )
// public abstract void slide()
// public abstract int putRequires()
//------------------------------------------------------------------
/**
* position から始まるデータパタンを
* LzssSearchMethod の持つ検索機構に登録する。
* LzssOutputStream は 線形に、重複無く、
* put または searchAndPut を呼び出す。
*
* @param position TextBuffer内のデータパタンの開始位置
*/
public abstract void put( int position );
/**
* 検索機構に登録されたデータパタンから
* position から始まるデータパタンと
* 最長の一致を持つものを検索し、
* 同時に position から始まるデータパタンを
* LzssSearchMethod の持つ検索機構に登録する。
* LzssOutputStream は 線形に、重複無く、
* put または searchAndPut を呼び出す。
*
* @param position TextBuffer内のデータパタンの開始位置
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public abstract int searchAndPut( int position );
/**
* 検索機構に登録されたデータパタンから
* position から始まるデータパタンと
* 最長の一致を持つものを検索する。
* このメソッドは LzssOutputStream の
* flush() を実装するためだけに提供される。
* TextBuffer.length < position + MaxMatch となるような
* position にも対応すること。
*
* @param position TextBuffer内のデータパタンの開始位置
* @param lastPutPos 最後に登録したデータパタンの開始位置
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public abstract int search( int position, int lastPutPos );
/**
* LzssOutputStream が slide() でTextBuffer内のデータを
* DictionarySize だけ移動させる際に検索機構内のデータを
* それらと矛盾無く移動させる処理を行う。
*/
public abstract void slide();
/**
* put() または searchAndPut() を使用して
* データパタンを検索機構に登録する時に
* 必要とするデータ量を得る。
*
* @return put() または searchAndPut() で
* 検索機構に登録するデータ量
*/
public abstract int putRequires();
}
//end of LzssSearchMethod.java
jp/gr/java_conf/dangan/util/lha/PatriciaTrieSearch.java 100644 0 0 117644 7575465247 20560 0 ustar 0 0 //start of PatriciaTrieSearch.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PatriciaTrieSearch.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.math.BigInteger;
import jp.gr.java_conf.dangan.io.Bits;
import jp.gr.java_conf.dangan.util.lha.LzssOutputStream;
import jp.gr.java_conf.dangan.util.lha.LzssSearchMethod;
//import exceptions
/**
* PATRICIA Trie を使用した LzssSearchMethod の実装。
*
*
* -- revision history --
* $Log: PatriciaTrieSearch.java,v $
* Revision 1.2 2002/12/10 22:28:55 dangan
* [bug fix]
* put( DictionarySize * 2 )
* searchAndPut( DictionarySize * 2 ) に対応していなかったのを修正。
*
* Revision 1.1 2002/12/04 00:00:00 dangan
* [change]
* LzssSearchMethod のインタフェイス変更に合わせてインタフェイス変更。
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/08/15 00:00:00 dangan
* add to version control
* [bug fix]
* contractNode で hashtable からの連結リストに繋ぐのを忘れていた修正。
* 配列 に PatriciaTrieSearch.ROOT_NODE(-1) でアクセスしていたのを修正。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.2 $
*/
public class PatriciaTrieSearch implements LzssSearchMethod{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// private static final int UNUSED
//------------------------------------------------------------------
/**
* 使用されていない事を示す値。
* parent[node] に UNUSED がある場合は node は未使用の node である。
* prev[node], next[node] に UNUSED がある場合は、
* そちら側に兄弟ノードが無いことを示す。
*/
private static final int UNUSED = 0;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private int DictionarySize
// private int MaxMatch
// private int Threshold
//------------------------------------------------------------------
/**
* LZSS 辞書サイズ
*/
private int DictionarySize;
/**
* LZSS 圧縮に使用される値。
* 最大一致長を示す。
*/
private int MaxMatch;
/**
* LZSS 圧縮/非圧縮の閾値。
* 一致長が この値以上であれば、圧縮コードを出力する。
*/
private int Threshold;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// Text Buffer
//------------------------------------------------------------------
// private byte[] TextBuffer
// private int DictionaryLimit
//------------------------------------------------------------------
/**
* LZSS 圧縮を施すためのバッファ。
* LzssSearchMethod の実装内では読み込みのみ許される。
*/
private byte[] TextBuffer;
/**
* 辞書の限界位置。
* TextBuffer前半の辞書領域に未だにデータが無い場合に
* 辞書領域にある不定のデータ(Java では0)を使用して
* 圧縮が行われないようにする。
*/
private int DictionaryLimit;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// PATRICIA TRIE
//------------------------------------------------------------------
// private int[] parent
// private int[] hashTable
// private int[] prev
// private int[] next
// private int[] position
// private int[] level
// private int[] childnum
// private int avail
// private int shift
//------------------------------------------------------------------
/**
* 親のノード番号を示す。
* parent[node] は node の親ノードの番号を示す。
*/
private int[] parent;
/**
* 子のハッシュ値を示す。
* hashTable[ hash( node, ch ) ] で
* node の文字 ch の子ノードのハッシュ値を示す。
*/
private int[] hashTable;
/**
* hashTable から連なる双方向連結リストの一部。
* 同じハッシュ値を持つ 一つ前のノードのノード番号を示す。
* prev[ node ] は node と同じハッシュ値を持ち、
* 連結リスト内で node の一つ前に位置するノードの node 番号。
* prev[ node ] が 負値の場合は全ビット反転したハッシュ値を示す。
*/
private int[] prev;
/**
* hashTable から連なる双方向連結リストの一部。
* 同じハッシュ値を持つ 一つ後のノードのノード番号を示す。
* next[ node ] は node と同じハッシュ値を持ち、
* 連結リスト内で node の一つ後ろに位置するノードの node 番号。
*
* また、葉でないノードに関しては next と avail で 未使用なノードの
* スタック(一方向連結リスト)を構成する。
*
* さらに、完全一致があったため削除された葉ノードで、
* PATRICIA Trie 内に存在している葉ノードへの一方向連結リストを構成する。
*/
private int[] next;
/**
* ノードの TextBuffer 内のデータパタンの開始位置を示す。
* position[ node ] は node のデータパタンの開始位置を示す。
*/
private int[] position;
/**
* ノードの 分岐位置を示す。
* level[ node ] は node の子ノードが分岐する位置を示す。
*/
private int[] level;
/**
* ノードの子ノードの数を示す。
* childnum[ node ] は node の子ノードの数を示す。
*/
private int[] childnum;
/**
* next が構成する未使用ノードのスタックのスタックポインタ。
*/
private int avail;
/**
* ハッシュ時に使用するシフト値
*/
private int shift;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// private int lastMatchPos
// private int lastMatchLen
//------------------------------------------------------------------
/**
* 最後の searchAndPut() または put() で得られた
* 得られた PatriciaTrie内の最長一致位置
*/
private int lastMatchPos;
/**
* 最後の searchAndPut() または put() で
* 得られた PatriciaTrie内の最長一致長
*/
private int lastMatchLen;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private PatriciaTreeSearch()
// public PatriciaTreeSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private PatriciaTrieSearch(){ }
/**
* コンストラクタ。
* PATRICIA Trie を使用した検索機構を構築する。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最長一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
*/
public PatriciaTrieSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer ){
this.DictionarySize = DictionarySize;
this.MaxMatch = MaxMatch;
this.Threshold = Threshold;
this.TextBuffer = TextBuffer;
this.DictionaryLimit = this.DictionarySize;
this.parent = new int[ this.DictionarySize * 2 ];
this.prev = new int[ this.DictionarySize * 2 ];
this.next = new int[ this.DictionarySize * 2 ];
this.position = new int[ this.DictionarySize ];
this.level = new int[ this.DictionarySize ];
this.childnum = new int[ this.DictionarySize ];
this.hashTable = new int[
PatriciaTrieSearch.generateProbablePrime(
this.DictionarySize + ( this.DictionarySize >> 2 ) ) ];
for( int i = 2 ; i < this.DictionarySize ; i++ ){
this.next[i] = i - 1;
}
this.avail = this.DictionarySize - 1;
for( int i = 0 ; i < this.DictionarySize * 2 ; i++ ){
this.parent[ i ] = PatriciaTrieSearch.UNUSED;
}
for( int i = 0 ; i < this.hashTable.length ; i++ ){
this.hashTable[ i ] = PatriciaTrieSearch.UNUSED;
}
this.shift = Bits.len( this.DictionarySize ) - 8;
this.lastMatchLen = 0;
this.lastMatchPos = 0;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.LzssSearchMethod
//------------------------------------------------------------------
// public void put( int position )
// public int searchAndPut( int position )
// public int search( int position, int lastPutPos )
// public void slide( int slideWidth, int slideEnd )
// public int putRequires()
//------------------------------------------------------------------
/**
* position から始まるデータパタンを
* PATRICIA Trie に登録する。
*
* @param position TextBuffer内のデータパタンの開始位置
*/
public void put( int position ){
//------------------------------------------------------------------
// PATRICIA Trie から最も古いデータパタンを削除
int posnode = ( position & ( this.DictionarySize - 1 ) ) + this.DictionarySize;
this.deleteNode( posnode );
//------------------------------------------------------------------
// PATRICIA Trie から最長一致を検索
int matchnode = -1;
int matchpos = position;
int scannode;
int matchlen;
if( 3 < this.lastMatchLen ){
//前回の一致長が閾値より大きければ、
//葉から lastMatchLen - 1 の一致を検索する。
scannode = ( this.lastMatchPos + 1 ) | this.DictionarySize;
//最長一致があったために scannode が
//PATRICIA Trie から取り除かれている場合の処理
while( this.parent[ scannode ] == PatriciaTrieSearch.UNUSED ){
scannode = this.next[ scannode ];
}
//葉から 順番に親へと辿って
//lastMatchLen - 1 以下の level を持つノードを探す。
int node = this.parent[ scannode ];
this.lastMatchLen--;
while( 0 < node
&& this.lastMatchLen <= this.level[ node ] ){
scannode = node;
node = this.parent[ node ];
}
//さらに親へと辿って position を更新していく。
while( 0 < node ){
this.position[ node ] = position;
node = this.parent[ node ];
}
matchlen = this.lastMatchLen;
}else{
//PATRICIA Trie を 根から辿る。
scannode = this.child( this.TextBuffer[ position ] - 128,
this.TextBuffer[ position + 1 ] & 0xFF );
matchlen = 2;
if( scannode == PatriciaTrieSearch.UNUSED ){
//根に position を追加する。
this.attachNode( this.TextBuffer[ position ] - 128, posnode,
this.TextBuffer[ position + 1 ] & 0xFF );
this.lastMatchLen = matchlen;
return;
}
}
while( true ){
int max;
if( scannode < this.DictionarySize ){
max = this.level[ scannode ];
matchnode = scannode;
matchpos = this.position[ scannode ];
}else{
max = this.MaxMatch;
matchnode = scannode;
matchpos = ( position <= scannode
? scannode - this.DictionarySize
: scannode );
}
while( matchlen < max
&& ( this.TextBuffer[ matchpos + matchlen ]
== this.TextBuffer[ position + matchlen ] ) ){
matchlen++;
}
if( matchlen == max && matchlen < this.MaxMatch ){
this.position[ scannode ] = position;
scannode = this.child( scannode,
this.TextBuffer[ position + matchlen ] & 0xFF );
if( scannode == PatriciaTrieSearch.UNUSED ){
this.attachNode( matchnode, posnode,
this.TextBuffer[ position + matchlen ] & 0xFF );
break;
}else{
matchlen++;
}
}else if( matchlen < max ){
//matchnode と position を分岐させる。
this.splitNode( matchnode, matchpos, posnode, position, matchlen );
break;
}else{
//完全一致を発見、ノードを置き換える。
this.replaceNode( matchnode, posnode );
this.next[ matchnode ] = position;
break;
}
}
//検索結果を保存
this.lastMatchLen = matchlen;
this.lastMatchPos = matchpos;
}
/**
* PATRICIA Trie に登録されたデータパタンから
* position から始まるデータパタンと
* 最長の一致を持つものを検索し、
* 同時に position から始まるデータパタンを
* PATRICIA Trie に登録する。
*
* @param position TextBuffer内のデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int searchAndPut( int position ){
//------------------------------------------------------------------
// PATRICIA Trie から最も古いデータパタンを削除
int posnode = ( position & ( this.DictionarySize - 1 ) ) + this.DictionarySize;
this.deleteNode( posnode );
//------------------------------------------------------------------
// PATRICIA Trie から最長一致を検索
int matchnode = -1;
int matchpos = position;
int scannode = 0;
int matchlen = 0;
if( 3 < this.lastMatchLen ){
//前回の一致長が閾値より大きければ、
//葉から lastMatchLen - 1 の一致を検索する。
scannode = ( this.lastMatchPos + 1 ) | this.DictionarySize;
//最長一致があったために scannode が
//PATRICIA Trie から取り除かれている場合の処理
while( this.parent[ scannode ] == PatriciaTrieSearch.UNUSED ){
scannode = this.next[ scannode ];
}
//葉から 順番に親へと辿って
//lastMatchLen - 1 以下の level を持つノードを探す。
int node = this.parent[ scannode ];
this.lastMatchLen--;
while( 0 < node
&& this.lastMatchLen <= this.level[ node ] ){
scannode = node;
node = this.parent[ node ];
}
//さらに親へと辿って position を更新していく。
while( 0 < node ){
this.position[ node ] = position;
node = this.parent[ node ];
}
matchlen = this.lastMatchLen;
}else{
//PATRICIA Trie を 根から辿る。
scannode = this.child( this.TextBuffer[ position ] - 128,
this.TextBuffer[ position + 1 ] & 0xFF );
matchlen = 2;
}
// scannode == UNUSED となるのは lastMatchLen が閾値より小さいときのみ。
if( scannode != PatriciaTrieSearch.UNUSED ){
while( true ){
int max;
if( scannode < this.DictionarySize ){
max = this.level[ scannode ];
matchnode = scannode;
matchpos = this.position[ scannode ];
}else{
max = this.MaxMatch;
matchnode = scannode;
matchpos = ( position <= scannode
? scannode - this.DictionarySize
: scannode );
}
while( matchlen < max
&& ( this.TextBuffer[ matchpos + matchlen ]
== this.TextBuffer[ position + matchlen ] ) ){
matchlen++;
}
if( matchlen == max && matchlen < this.MaxMatch ){
this.position[ scannode ] = position;
scannode = this.child( scannode,
this.TextBuffer[ position + matchlen ] & 0xFF );
if( scannode == PatriciaTrieSearch.UNUSED ){
//matchnode に position を追加する。
this.attachNode( matchnode, posnode,
this.TextBuffer[ position + matchlen ] & 0xFF );
break;
}else{
matchlen++;
}
}else if( matchlen < max ){
//matchnode と position を分岐させる。
this.splitNode( matchnode, matchpos, posnode, position, matchlen );
break;
}else{
//完全一致を発見、ノードを置き換える。
this.replaceNode( matchnode, posnode );
this.next[ matchnode ] = position;
break;
}
}
}else{ //if( scannode != PatriciaTrieSearch.UNUSED )
//根に position を追加する。
this.attachNode( this.TextBuffer[ position ] - 128, posnode,
this.TextBuffer[ position + 1 ] & 0xFF );
matchlen = 0;
}
//検索結果を保存
this.lastMatchLen = matchlen;
this.lastMatchPos = matchpos;
//------------------------------------------------------------------
// メソッド先頭で PATRICIA Trie から削除したデータパタンもチェックする。
scannode = position - this.DictionarySize;
if( this.DictionaryLimit <= scannode ){
int len = 0;
while( this.TextBuffer[ scannode + len ]
== this.TextBuffer[ position + len ] )
if( this.MaxMatch <= ++len ) break;
if( matchlen < len ){
matchpos = scannode;
matchlen = len;
}
}
//------------------------------------------------------------------
// 最長一致を呼び出し元に返す。
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* PATRICIA Trie に登録されたデータパタンを検索し
* position から始まるデータパタンと
* 最長の一致を持つものを得る。
*
* @param position TextBuffer内のデータパタンの開始位置。
* @param lastPutPos 最後に登録したデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int search( int position, int lastPutPos ){
//------------------------------------------------------------------
// PATRICIA Trie に登録されていないデータパタンを
// 単純な逐次検索で検索する。
int scanlimit = Math.max( this.DictionaryLimit, lastPutPos );
int scanpos = position - 1;
int matchlen = 0;
int matchpos = 0;
byte[] buf = this.TextBuffer;
int max = Math.min( this.TextBuffer.length,
position + this.MaxMatch );
int s = 0;
int p = 0;
int len = 0;
while( scanlimit < scanpos ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
if( max <= p ) break;
}
scanpos--;
}
//------------------------------------------------------------------
// PATRICIA Trie を探索
if( 2 < this.TextBuffer.length - position ){
int matchnode = this.child( this.TextBuffer[ position ] - 128,
this.TextBuffer[ position + 1 ] & 0xFF );
scanlimit = Math.max( this.DictionaryLimit,
position - this.DictionarySize );
len = 2;
while( matchnode != PatriciaTrieSearch.UNUSED ){
int maxlen;
if( matchnode < this.DictionarySize ){
maxlen = this.level[ matchnode ];
scanpos = this.position[ matchnode ];
}else{
maxlen = this.MaxMatch;
scanpos = ( lastPutPos < matchnode
? matchnode - this.DictionarySize
: matchnode );
}
if( scanlimit <= scanpos ){
max = Math.min( this.TextBuffer.length,
position + maxlen );
s = scanpos + len;
p = position + len;
if( p < max ){
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
}
if( len == maxlen && matchlen < this.MaxMatch ){
if( position + len < this.TextBuffer.length ){
matchnode = this.child( matchnode,
this.TextBuffer[ position + len ] & 0xFF );
if( matchnode != PatriciaTrieSearch.UNUSED ){
len++;
}
}else{
break;
}
}else{ //maxlen に満たない一致が見つかったか 完全一致が見つかった
break;
}
}else{ //if( scanlimit <= scanpos ) 一致したパタンは検索限界を超えていた。
break;
}
} //while( matchnode != PatriciaTrieSearch.UNUSED )
} //if( 2 <= this.TextBuffer.length - position )
//------------------------------------------------------------------
// 最長一致を呼び出し元に返す。
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* TextBuffer内のpositionまでのデータを
* 前方へ移動する際、それに応じて LzssSearchMethod
* 内のデータも TextBuffer内のデータと矛盾しないよ
* うに前方へ移動する処理を行う。
*/
public void slide(){
this.DictionaryLimit = Math.max( 0, this.DictionaryLimit - this.DictionarySize );
this.lastMatchPos -= this.DictionarySize;
for( int i = 0 ; i < this.position.length ; i++ ){
int pos = this.position[i] - this.DictionarySize;
if( 0 < pos ){
this.position[i] = pos;
}else{
this.position[i] = 0;
}
}
}
/**
* put() で LzssSearchMethodにデータを
* 登録するときに使用されるデータ量を得る。
* PatriciaTrieSearch では、常に MaxMatch を返す。
*
* @return 常に MaxMatch
*/
public int putRequires(){
return this.MaxMatch;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// manipulate node
//------------------------------------------------------------------
// private void splitNode( int oldnode, int oldpos, int position, int splitLen )
// private void deleteNode( int node )
// private void attatchNode( int parentnode, int childnode, int pos )
// private void replaceNode( int oldnode, int newnode )
// private void contractNode( int node )
//------------------------------------------------------------------
/**
* oldnode を splitLen で分岐させる。
* oldnode のあった位置には新しいノードが新設され、
* 新しいノードは oldnode と position を子に持つ。
*
* @param oldnode 分岐させるノード
* @param oldpos oldnode が指すデータパタンの開始位置
* @param posnode position 用ノード
* @param position TextBuffer 内のデータパタンの開始位置
* @param splitLen データパタン内の分岐位置
*/
private void splitNode( int oldnode, int oldpos, int posnode, int position, int splitLen ){
//スタックから 新しいノードを取得する。
int newnode = this.avail;
this.avail = this.next[ newnode ];
this.replaceNode( oldnode, newnode );
this.level[ newnode ] = splitLen;
this.position[ newnode ] = position;
this.childnum[ newnode ] = 0;
this.attachNode( newnode, oldnode,
this.TextBuffer[ oldpos + splitLen ] & 0xFF );
this.attachNode( newnode, posnode,
this.TextBuffer[ position + splitLen ] & 0xFF );
}
/**
* PATRICIA Trie から葉である node を削除する。
* 必要であれば node の親ノードの繰上げ処理も行う。
*
* @param node 削除する葉ノード
*/
private void deleteNode( int node ){
if( this.parent[ node ] != PatriciaTrieSearch.UNUSED ){
int parent = this.parent[ node ];
int prev = this.prev[ node ];
int next = this.next[ node ];
this.parent[ node ] = PatriciaTrieSearch.UNUSED;
this.prev[ node ] = PatriciaTrieSearch.UNUSED;
this.next[ node ] = PatriciaTrieSearch.UNUSED;
if( 0 <= prev ){
this.next[ prev ] = next;
}else{
this.hashTable[ ~prev ] = next;
}
this.prev[ next ] = prev;
if( 0 < parent ){ //parent が PATRICIA Trie の根で無い場合 true となる条件式
this.childnum[ parent ]--;
if( this.childnum[ parent ] <= 1 ){
this.contractNode( this.child( parent,
this.TextBuffer[ this.position[ parent ]
+ this.level[ parent ] ]
& 0xFF ) );
}
}
}
}
/**
* parentnode に childnode を追加する。
*
* @param parentnode childnode を追加する対象の親ノード
* @param childnode parentnode に追加するノード
* @param pos TextBuffer内現在処理位置。
* 葉の position を確定するために使用される。
*/
private void attachNode( int parentnode, int childnode, int ch ){
int hash = this.hash( parentnode, ch );
int node = this.hashTable[ hash ];
this.hashTable[ hash ] = childnode;
this.parent[ childnode ] = parentnode;
this.prev[ childnode ] = ~hash;
this.next[ childnode ] = node;
this.prev[ node ] = childnode;
if( 0 < parentnode ){
this.childnum[ parentnode ]++;
}
}
/**
* oldnode と newnode を入れ替える。
* newnode は子ノードとの関係を保持する。
* oldnode は置き換えられて PATRICIA Trie から取り除かれる。
*
* @param oldnode 入れ替えられて Trie から削除されるノード
* @param newnode oldnode のあった位置へ接続されるノード
*/
private void replaceNode( int oldnode, int newnode ){
this.parent[ newnode ] = this.parent[ oldnode ];
this.prev[ newnode ] = this.prev[ oldnode ];
this.next[ newnode ] = this.next[ oldnode ];
this.prev[ this.next[ newnode ] ] = newnode;
if( this.prev[ newnode ] < 0 ){
this.hashTable[ ~this.prev[ newnode ] ] = newnode;
}else{
this.next[ this.prev[ newnode ] ] = newnode;
}
this.parent[ oldnode ] = PatriciaTrieSearch.UNUSED;
this.prev[ oldnode ] = PatriciaTrieSearch.UNUSED;
this.next[ oldnode ] = PatriciaTrieSearch.UNUSED;
}
/**
* 兄弟の無くなった node を引き上げる。
* node の親ノードは PATRICIA Trie から削除され、
* 代わりに node がその位置に接続される。
* 兄弟が無いかどうかの 判定は呼び出し側が行う。
*
* @param node 引き上げるノード
*/
private void contractNode( int node ){
int parentnode = this.parent[ node ];
this.prev[ this.next[ node ] ] = this.prev[ node ];
if( 0 <= this.prev[ node ] ){
this.next[ this.prev[ node ] ] = this.next[ node ];
}else{
this.hashTable[ ~ this.prev[ node ] ] = this.next[ node ];
}
this.replaceNode( parentnode, node );
//使用されなくなった parentnode をスタックに返還する。
this.next[ parentnode ] = this.avail;
this.avail = parentnode;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void slideTree( int[] src, int[] dst, int width )
// private int child( int parent, int ch )
// private int hash( int node, int ch )
//------------------------------------------------------------------
/**
* slide 時に Trie の各要素を移動させる処理を行う。
*
* @param src 移動元
* @param dst 移動先
* @param width 移動幅
*/
private void slideTree( int[] src, int[] dst, int width ){
for( int i = 0 ; i < this.DictionarySize ; i++ )
dst[i] = ( src[ i ] < this.DictionarySize
? src[ i ]
: ( ( src[ i ] - width ) & ( this.DictionarySize - 1 ) )
+ this.DictionarySize );
for( int i = this.DictionarySize ; i < src.length ; i++ )
dst[ ( ( i - width ) & ( this.DictionarySize - 1 ) )
+ this.DictionarySize ] = ( src[ i ] < this.DictionarySize
? src[ i ]
: ( ( src[ i ] - width )
& ( this.DictionarySize - 1 ) )
+ this.DictionarySize );
}
/**
* parent から ch で分岐した子を得る。
* ノードが無い場合は UNUSED を返す。
*
* @param parent 親ノード
* @param ch 分岐文字
*
* @return 子ノード
*/
private int child( int parent, int ch ){
int node = this.hashTable[ this.hash( parent, ch ) ];
//this.parent[ PatriciaTrieSearch.UNUSED ] = parent;
while( node != PatriciaTrieSearch.UNUSED
&& this.parent[ node ] != parent ){
node = this.next[ node ];
}
return node;
}
/**
* node と ch から ハッシュ値を得る
*
* @param node ノード
* @param ch 分岐文字
*
* @return ハッシュ値
*/
private int hash( int node, int ch ){
return ( node + ( ch << this.shift ) + 256 ) % this.hashTable.length;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// generate prime
//------------------------------------------------------------------
// private static int generateProbablePrime( int num )
//------------------------------------------------------------------
/**
* num 以上の最も小さい 素数(もしくは擬似素数)を生成する。
* 戻り値が 素数でない確率は 1/256 以下である。
*
* @param num この値以上の素数を生成する。
*
* @return 生成された素数(もしくは擬似素数)
*/
private static int generateProbablePrime( int num ){
num = num + ( ( num & 1 ) == 0 ? 1 : 0 );
while( !(new BigInteger(Integer.toString(num))).isProbablePrime( 8 ) ){
num += 2;
num = num + ( ( num % 3 ) == 0 ? 2 : 0 );
num = num + ( ( num % 5 ) == 0 ? 2 : 0 );
num = num + ( ( num % 7 ) == 0 ? 2 : 0 );
}
return num;
}
}
//
//
// //------------------------------------------------------------------
// // local method
// //------------------------------------------------------------------
// // check
// //------------------------------------------------------------------
// // private void checkTrie( int pos )
// // private void checkNode( int node, int pos )
// // private void writeNode( int node )
// //------------------------------------------------------------------
// /**
// * Trie全体のチェックを行う。
// *
// * @param pos 現在処理位置。
// *
// * @exception RuntimeException Trie が崩れていた場合。
// */
// private void checkTrie( int pos ){
// for( int i = -256 ; i < 0 ; i++ ){
// this.checkNode( i, pos );
// }
//
// for( int i = 1 ; i < this.DictionarySize ; i++ ){
// if( this.parent[ i ] != PatriciaTrieSearch.UNUSED ){
// this.checkNode( i, pos );
// }
// }
// }
//
// /**
// * 葉でない Node のチェックを行う。
// *
// * チェック項目は
// * (1) 親子関係
// * (2) position に矛盾が無い事。
// * (3) level に矛盾が無い事。
// * (4) node が this.childnum[node] 個の子供を持っている事。
// * の4項目。
// *
// * @param node チェックするノード
// * @param pos 現在処理位置
// *
// * @exception RuntimeException 上記のチェックの何れかが失敗した場合。
// */
// private void checkNode( int node, int pos ){
//
// int nlevel;
// int npos;
// if( node < 0 ){
// nlevel = 0;
// npos = this.TextBuffer.length;
// }else{
// nlevel = this.level[ node ];
// npos = this.position[ node ];
// }
//
// int childcount = 0;
// for( int i = 0 ; i < 256 ; i++ ){
// int child = this.child( node, i );
//
// if( child != PatriciaTrieSearch.UNUSED ){
// childcount++;
//
// if( this.parent[ child ] != node ){
// System.out.println( "unlink::parent<->child" );
// this.writeNode( node );
// this.writeNode( child );
// throw new RuntimeException( "Trie Broken" );
// }
//
// if( child < this.DictionarySize ){
// if( this.level[ child ] <= nlevel ){
// System.out.println( "broken hierarchy::level" );
// this.writeNode( node );
// this.writeNode( child );
// throw new RuntimeException( "Trie Broken" );
// }
//
// if( npos < this.position[ child ] ){
// System.out.println( "broken hierarchy::position" );
// this.writeNode( node );
// this.writeNode( child );
// throw new RuntimeException( "Trie Broken" );
// }
// //this.checkTrie( child, pos );
// }else{
// int childpos = ( pos <= child ? child - this.DictionarySize : child );
// if( npos < childpos ){
// System.out.println( "broken hierarchy::position" );
// this.writeNode( node );
// this.writeNode( child );
// throw new RuntimeException( "Trie Broken" );
// }
// }
// }
// }
//
// if( 0 < node && node < this.DictionarySize ){
// if( this.childnum[ node ] != childcount ){
// System.out.println( "broken hierarchy::childnum" );
// this.writeNode( node );
// throw new RuntimeException( "Trie Broken" );
// }
// }
// }
//
// /**
// * ノードの情報を出力する。
// *
// * @param node 情報を出力するノード
// */
// private void writeNode( int node ){
// if( 0 < node ){
// System.out.println( "this.parent[" + node + "] ::" + this.parent[ node ] );
// System.out.println( "this.prev[" + node + "] ::" + this.prev[ node ] );
// System.out.println( "this.next[" + node + "] ::" + this.next[ node ] );
// if( node < this.DictionarySize ){
// System.out.println( "this.childnum[" + node + "]::" + this.childnum[ node ] );
// System.out.println( "this.position[" + node + "]::" + this.position[ node ] );
// System.out.println( "this.level[" + node + "] ::" + this.level[ node ] );
// }
// }else if( node < 0 ){
// System.out.println( "ROOT_NODE ::" + node );
// }else{
// System.out.println( "UNUSED ::" + node );
// }
//
// }
//end of PatriciaTrieSearch.java
jp/gr/java_conf/dangan/util/lha/PostLh1Encoder.java 100644 0 0 25735 7572250400 17577 0 ustar 0 0 //start of PostLh1Encoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PostLh1Encoder.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.OutputStream;
import jp.gr.java_conf.dangan.io.BitOutputStream;
import jp.gr.java_conf.dangan.util.lha.StaticHuffman;
import jp.gr.java_conf.dangan.util.lha.DynamicHuffman;
import jp.gr.java_conf.dangan.util.lha.PostLzssEncoder;
//import exceptions
import java.io.IOException;
import java.lang.NullPointerException;
import jp.gr.java_conf.dangan.util.lha.BadHuffmanTableException;
/**
* -lh1- 圧縮用の PostLzssEncoder。
*
*
* -- revision history --
* $Log: PostLh1Encoder.java,v $
* Revision 1.1 2002/12/01 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/07/31 00:00:00 dangan
* add to version control
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class PostLh1Encoder implements PostLzssEncoder{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private static final int DictionarySize
// private static final int MaxMatch
// private static final int Threshold
//------------------------------------------------------------------
/** 辞書サイズ */
private static final int DictionarySize = 4096;
/** 最大一致長 */
private static final int MaxMatch = 60;
/** 最小一致長 */
private static final int Threshold = 3;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private BitOutputStream out
//------------------------------------------------------------------
/**
* -lh1- 形式の圧縮データの出力先の ビット出力ストリーム
*/
private BitOutputStream out;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// dynamic huffman tree
//------------------------------------------------------------------
// private DynamicHuffman huffman
//------------------------------------------------------------------
/**
* Code部圧縮用適応的ハフマン木
*/
private DynamicHuffman huffman;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// static huffman list
//------------------------------------------------------------------
// private int[] offHiCode
// private int[] offHiLen
//------------------------------------------------------------------
/**
* offset部の上位6bit圧縮用ハフマン符号の表
*/
private int[] offHiCode;
/**
* offset部の上位6bit圧縮用ハフマン符号長の表
*/
private int[] offHiLen;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private PostLh1Encoder()
// public PostLh1Encoder( OutputStream out )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private PostLh1Encoder(){ }
/**
* -lh1- 圧縮用 PostLzssEncoder を構築する。
*
* @param out 圧縮データを受け取る出力ストリーム
*/
public PostLh1Encoder( OutputStream out ){
if( out != null ){
if( out instanceof BitOutputStream ){
this.out = (BitOutputStream)out;
}else{
this.out = new BitOutputStream( out );
}
this.huffman = new DynamicHuffman( 314 );
this.offHiLen = PostLh1Encoder.createLenList();
try{
this.offHiCode = StaticHuffman.LenListToCodeList( this.offHiLen );
}catch( BadHuffmanTableException exception ){
}
}else{
throw new NullPointerException( "out" );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void writeCode( int code )
// public void writeOffset( int offset )
//------------------------------------------------------------------
/**
* 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長を書きこむ。
*
* @param code 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
*/
public void writeCode( int code ) throws IOException {
int node = this.huffman.codeToNode( code );
int hcode = 0;
int hlen = 0;
do{
hcode >>>= 1;
hlen++;
if( ( node & 1 ) != 0 ) hcode |= 0x80000000;
node = this.huffman.parentNode( node );
}while( node != DynamicHuffman.ROOT );
this.out.writeBits( hlen, hcode >> ( 32 - hlen ) ); //throws IOException
this.huffman.update( code );
}
/**
* LZSS で圧縮された圧縮コードのうち一致位置を書きこむ。
*
* @param offset LZSS で圧縮された圧縮コードのうち一致位置
*/
public void writeOffset( int offset ) throws IOException {
int offHi = ( offset >> 6 );
this.out.writeBits( this.offHiLen[offHi], this.offHiCode[offHi] ); //throws IOException
this.out.writeBits( 6, offset ); //throws IOException
}
//------------------------------------------------------------------
// method jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* この PostLzssEncoder にバッファリングされている
* 全ての 8ビット単位のデータを出力先の OutputStream に出力し、
* 出力先の OutputStream を flush() する。
* このメソッドは圧縮率を変化させない。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PostLzssEncoder#flush()
* @see BitOutputStream#flush()
*/
public void flush() throws IOException {
this.out.flush(); //throws IOException
}
/**
* この出力ストリームと、接続された出力ストリームを閉じ、
* 使用していたリソースを解放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.out.close(); //throws IOException
this.out = null;
this.huffman = null;
this.offHiLen = null;
this.offHiCode = null;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* -lh1-形式の LZSS辞書のサイズを得る。
*
* @return -lh1-形式の LZSS辞書のサイズ
*/
public int getDictionarySize(){
return PostLh1Encoder.DictionarySize;
}
/**
* -lh1-形式の LZSSの最大一致長を得る。
*
* @return -lz5-形式の LZSSの最大一致長
*/
public int getMaxMatch(){
return PostLh1Encoder.MaxMatch;
}
/**
* -lh1-形式の LZSSの圧縮、非圧縮の閾値を得る。
*
* @return -lh1-形式の LZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return PostLh1Encoder.Threshold;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private static int[] createLenList()
//------------------------------------------------------------------
/**
* -lh1- の offsetデコード用StaticHuffmanの
* ハフマン符号長リストを生成する。
*
* @return -lh1- の offsetデコード用StaticHuffmanの
* ハフマン符号長リスト
*/
private static int[] createLenList(){
final int length = 64;
final int[] list = { 3, 0x01, 0x04, 0x0C, 0x18, 0x30, 0 };
int[] LenList = new int[ length ];
int index = 0;
int len = list[ index++ ];
for( int i = 0 ; i < length ; i++ ){
if( list[index] == i ){
len++;
index++;
}
LenList[i] = len;
}
return LenList;
}
}
//end of PostLh1Encoder.java
jp/gr/java_conf/dangan/util/lha/PostLh2Encoder.java 100644 0 0 27512 7572250400 17573 0 ustar 0 0 //start of PostLh2Encoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PostLh2Encoder.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.OutputStream;
import java.lang.Math;
import jp.gr.java_conf.dangan.io.BitOutputStream;
import jp.gr.java_conf.dangan.util.lha.DynamicHuffman;
import jp.gr.java_conf.dangan.util.lha.PostLzssEncoder;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.lang.NullPointerException;
/**
* -lh2- 圧縮用 PostLzssEncoder。
*
*
* -- revision history --
* $Log: PostLh2Encoder.java,v $
* Revision 1.1 2002/12/01 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/07/31 00:00:00 dangan
* add to version control
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class PostLh2Encoder implements PostLzssEncoder {
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private static final int DictionarySize
// private static final int MaxMatch
// private static final int Threshold
//------------------------------------------------------------------
/** 辞書サイズ */
private static final int DictionarySize = 8192;
/** 最大一致長 */
private static final int MaxMatch = 256;
/** 最小一致長 */
private static final int Threshold = 3;
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// private static final int CodeSize
//------------------------------------------------------------------
/**
* code部のハフマン木のサイズ
* code部がこれ以上の値を扱う場合は余計なビットを出力して補う。
*/
private static final int CodeSize = 286;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private BitOutputStream out
//------------------------------------------------------------------
/**
* -lh2- 形式の圧縮データの出力先の ビット出力ストリーム
*/
private BitOutputStream out;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// dynamic huffman tree
//------------------------------------------------------------------
// private DynamicHuffman codeHuffman
// private DynamicHuffman offHiHuffman
//------------------------------------------------------------------
/**
* code部圧縮用適応的ハフマン木
*/
private DynamicHuffman codeHuffman;
/**
* offHi部圧縮用適応的ハフマン木
*/
private DynamicHuffman offHiHuffman;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// current position
//------------------------------------------------------------------
// private int position
// private int nextPosition
// private int matchLength
//------------------------------------------------------------------
/**
* 出力したオリジナルのサイズをカウントする
*/
private int position;
/**
* 次に葉を加える動作をする位置
*/
private int nextPosition;
/**
* 現在処理中の一致長
*/
private int matchLength;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private PostLh2Encoder()
// public PostLh2Encoder( OutputStream out )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private PostLh2Encoder(){ }
/**
* -lh2- 圧縮用 PostLzssEncoder を構築する。
*
* @param out 圧縮データを受け取る出力ストリーム
*/
public PostLh2Encoder( OutputStream out ){
if( out != null ){
if( out instanceof BitOutputStream ){
this.out = (BitOutputStream)out;
}else{
this.out = new BitOutputStream( out );
}
this.codeHuffman = new DynamicHuffman( PostLh2Encoder.CodeSize );
this.offHiHuffman = new DynamicHuffman(
PostLh2Encoder.DictionarySize >> 6, 1 );
this.position = 0;
this.nextPosition = 1 << 6;
}else{
throw new NullPointerException( "out" );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void writeCode( int code )
// public void writeOffset( int offset )
//------------------------------------------------------------------
/**
* 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長を書きこむ。
*
* @param code 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
*/
public void writeCode( int code ) throws IOException {
final int CodeMax = PostLh2Encoder.CodeSize - 1;
int node = this.codeHuffman.codeToNode( Math.min( code, CodeMax ) );
int hcode = 0;
int hlen = 0;
do{
hcode >>>= 1;
hlen++;
if( ( node & 1 ) != 0 ) hcode |= 0x80000000;
node = this.codeHuffman.parentNode( node );
}while( node != DynamicHuffman.ROOT );
this.out.writeBits( hlen, hcode >>> ( 32 - hlen ) ); //throws IOException
if( code < 0x100 ){
this.position++;
}else{
this.matchLength = ( code & 0xFF ) + PostLh2Encoder.Threshold;
if( CodeMax <= code ){
this.out.writeBits( 8, code - CodeMax ); //throws IOException
code = CodeMax; //updateするコードをCodeMaxにする。
}
}
this.codeHuffman.update( code );
}
/**
* LZSS で圧縮された圧縮コードのうち一致位置を書きこむ。
*
* @param offset LZSS で圧縮された圧縮コードのうち一致位置
*/
public void writeOffset( int offset ) throws IOException {
if( this.nextPosition < PostLh2Encoder.DictionarySize ){
while( this.nextPosition < this.position ){
this.offHiHuffman.addLeaf( this.nextPosition >> 6 );
this.nextPosition += 64;
if( PostLh2Encoder.DictionarySize <= this.nextPosition )
break;
}
}
int node = this.offHiHuffman.codeToNode( offset >> 6 );
int hcode = 0;
int hlen = 0;
while( node != DynamicHuffman.ROOT ){
hcode >>>= 1;
hlen++;
if( ( node & 1 ) != 0 ) hcode |= 0x80000000;
node = this.offHiHuffman.parentNode( node );
}
this.out.writeBits( hlen, hcode >> ( 32 - hlen ) ); //throws IOException
this.out.writeBits( 6, offset ); //throws IOException
this.offHiHuffman.update( offset >> 6 );
this.position += this.matchLength;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* この PostLzssEncoder にバッファリングされている
* 全ての 8ビット単位のデータを出力先の OutputStream に出力し、
* 出力先の OutputStream を flush() する。
* このメソッドは圧縮率を変化させない。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PostLzssEncoder#flush()
* @see BitOutputStream#flush()
*/
public void flush() throws IOException {
this.out.flush(); //throws IOException
}
/**
* この出力ストリームと、接続された出力ストリームを閉じ、
* 使用していたリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.out.close(); //throws IOException
this.out = null;
this.codeHuffman = null;
this.offHiHuffman = null;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* -lh2-形式の LZSS辞書のサイズを得る。
*
* @return -lh2-形式の LZSS辞書のサイズ
*/
public int getDictionarySize(){
return PostLh2Encoder.DictionarySize;
}
/**
* -lh2-形式の LZSSの最大一致長を得る。
*
* @return -lh2-形式の LZSSの最大一致長
*/
public int getMaxMatch(){
return PostLh2Encoder.MaxMatch;
}
/**
* -lh2-形式の LZSSの圧縮、非圧縮の閾値を得る。
*
* @return -lh2-形式の LZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return PostLh2Encoder.Threshold;
}
}
//end of PostLh2Encoder.java
jp/gr/java_conf/dangan/util/lha/PostLh3Encoder.java 100644 0 0 55616 7573764200 17613 0 ustar 0 0 //start of PostLh3Encoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PostLh3Encoder.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.OutputStream;
import java.lang.Math;
import jp.gr.java_conf.dangan.io.BitOutputStream;
import jp.gr.java_conf.dangan.util.lha.StaticHuffman;
import jp.gr.java_conf.dangan.util.lha.PostLzssEncoder;
//import exceptions
import java.io.IOException;
import java.lang.NullPointerException;
import java.lang.IllegalArgumentException;
/**
* -lh3- 圧縮用 PostLzssEncoder。
*
*
* $Log: PostLh3Encoder.java,v $
* Revision 1.2 2002/12/06 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.1 2002/12/01 00:00:00 dangan
* [change]
* flush() されないかぎり
* 接続された OutputStream をflush() しないように変更。
* [maintenance]
* ソース整備。
*
* Revision 1.0 2002/07/31 00:00:00 dangan
* add to version control
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.2 $
*/
public class PostLh3Encoder implements PostLzssEncoder{
//------------------------------------------------------------------
// class fields
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private static final int DictionarySize
// private static final int MaxMatch
// private static final int Threshold
//------------------------------------------------------------------
/** 辞書サイズ */
private static final int DictionarySize = 8192;
/** 最大一致長 */
private static final int MaxMatch = 256;
/** 最小一致長 */
private static final int Threshold = 3;
//------------------------------------------------------------------
// class fields
//------------------------------------------------------------------
// private static final int[] ConstOffHiLen
// private static final int CodeSize
//------------------------------------------------------------------
/**
* OffHi部分の固定ハフマン符号長
*/
private static final int[] ConstOffHiLen = PostLh3Encoder.createConstOffHiLen();
/**
* code部のハフマン木のサイズ
* code部がこれ以上の値を扱う場合は余計なビットを出力して補う。
*/
private static final int CodeSize = 286;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private BitOutputStream out
//------------------------------------------------------------------
/**
* -lh3- 形式の圧縮データの出力先の ビット出力ストリーム
*/
private BitOutputStream out;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// buffer of LZSS codes
//------------------------------------------------------------------
// private byte[] buffer
// private int blockSize
// private int position
// private int flagBit
// private int flagPos
//------------------------------------------------------------------
/**
* 静的ハフマン圧縮するためにデータを一時的に貯えるバッファ
*/
private byte[] buffer;
/**
* バッファ内にある code データの数。
*/
private int blockSize;
/**
* buffer内の現在処理位置
*/
private int position;
/**
* flag バイト内の現在処理bit
*/
private int flagBit;
/**
* buffer内の現在のflagバイトの位置
*/
private int flagPos;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// frequancy counter for huffman
//------------------------------------------------------------------
// private int[] codeFreq
// private int[] offHiFreq
//------------------------------------------------------------------
/**
* code部の頻度表
*/
private int[] codeFreq;
/**
* offHi部の頻度表
*/
private int[] offHiFreq;
//------------------------------------------------------------------
// constructers
//------------------------------------------------------------------
// private PostLh3Encoder()
// public PostLh3Encoder( OutputStream out )
// public PostLh3Encoder( OutputStream out, int BufferSize )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private PostLh3Encoder(){ }
/**
* -lh3- 圧縮用 PostLzssEncoderを構築する。
* バッファサイズにはデフォルト値が使用される。
*
* @param out 圧縮データを受け取る出力ストリーム
*/
public PostLh3Encoder( OutputStream out ){
this( out, 16384 );
}
/**
* -lh3- 圧縮用 PostLzssEncoderを構築する。
*
* @param out 圧縮データを受け取る出力ストリーム
* @param BufferSize 静的ハフマン圧縮用のバッファサイズ
*
* @exception IllegalArgumentException
* BufferSize が小さすぎる場合
*/
public PostLh3Encoder( OutputStream out, int BufferSize ){
final int DictionarySizeByteLen = 2;
final int MinCapacity = ( DictionarySizeByteLen + 1 ) * 8 + 1;
if( out != null
&& MinCapacity <= BufferSize ){
if( out instanceof BitOutputStream ){
this.out = (BitOutputStream)out;
}else{
this.out = new BitOutputStream( out );
}
this.codeFreq = new int[ PostLh3Encoder.CodeSize ];
this.offHiFreq = new int[ PostLh3Encoder.DictionarySize >> 6 ];
this.buffer = new byte[ BufferSize ];
this.blockSize = 0;
this.position = 0;
this.flagBit = 0;
this.flagPos = 0;
}else if( out == null ){
throw new NullPointerException( "out" );
}else{
throw new IllegalArgumentException( "BufferSize too small. BufferSize must be larger than " + MinCapacity );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// write methods
//------------------------------------------------------------------
// public void writeCode( int code )
// public void writeOffset( int offset )
//------------------------------------------------------------------
/**
* 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長を書きこむ。
*
* @param code 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
*/
public void writeCode( int code ) throws IOException {
final int CodeMax = PostLh3Encoder.CodeSize - 1;
final int DictionarySizeByteLen = 2;
final int Capacity = ( DictionarySizeByteLen + 1 ) * 8 + 1;
if( this.flagBit == 0 ){
if( this.buffer.length - this.position < Capacity
|| ( 65536 - 8 ) <= this.blockSize ){
this.writeOut(); //throws IOException
}
this.flagBit = 0x80;
this.flagPos = this.position++;
this.buffer[ this.flagPos ] = 0;
}
//データ格納
this.buffer[this.position++] = (byte)code;
//上位1ビットをフラグとして格納
if( 0x100 <= code ) this.buffer[ this.flagPos ] |= this.flagBit;
this.flagBit >>= 1;
//頻度表更新
this.codeFreq[ Math.min( code, CodeMax ) ]++;
//ブロックサイズ更新
this.blockSize++;
}
/**
* LZSS で圧縮された圧縮コードのうち一致位置を書きこむ。
*
* @param offset LZSS で圧縮された圧縮コードのうち一致位置
*/
public void writeOffset( int offset ){
//データ格納
this.buffer[ this.position++ ] = (byte)( offset >> 8 );
this.buffer[ this.position++ ] = (byte)offset;
//頻度表更新
this.offHiFreq[ ( offset >> 6 ) ]++;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* この PostLzssEncoder にバッファリングされている全ての
* 8ビット単位のデータを出力先の OutputStream に出力し、
* 出力先の OutputStream を flush() する。
* このメソッドは圧縮率を変化させる。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PostLzssEncoder#flush()
* @see BitOutputStream#flush()
*/
public void flush() throws IOException {
this.writeOut();
this.out.flush();
}
/**
* この出力ストリームと、接続された出力ストリームを閉じ、
* 使用していたリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.writeOut();
this.out.close(); //throws IOException
this.out = null;
this.buffer = null;
this.codeFreq = null;
this.offHiFreq = null;
}
//------------------------------------------------------------------
// jp.gr.java_conf.dangan.util.lha.PostLzssEncoder methods
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* -lh3-形式の LZSS辞書のサイズを得る。
*
* @return -lh3-形式の LZSS辞書のサイズ
*/
public int getDictionarySize(){
return PostLh3Encoder.DictionarySize;
}
/**
* -lh3-形式の LZSSの最大一致長を得る。
*
* @return -lh3-形式の LZSSの最大一致長
*/
public int getMaxMatch(){
return PostLh3Encoder.MaxMatch;
}
/**
* -lh3-形式の LZSSの圧縮、非圧縮の閾値を得る。
*
* @return -lh3-形式の LZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return PostLh3Encoder.Threshold;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// write huffman code
//------------------------------------------------------------------
// private void writeOut()
//------------------------------------------------------------------
/**
* バッファリングされた全てのデータを this.out に出力する。
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOut() throws IOException {
final int CodeMax = PostLh3Encoder.CodeSize - 1;
if( 0 < this.blockSize ){
//------------------------------------------------------------------
// ブロックサイズ出力
this.out.writeBits( 16, this.blockSize ); //throws IOException
//------------------------------------------------------------------
// ハフマン符号表生成
int[] codeLen = StaticHuffman.FreqListToLenList( this.codeFreq );
int[] codeCode = StaticHuffman.LenListToCodeList( codeLen );
int[] offHiLen = PostLh3Encoder.getBetterOffHiLen( this.offHiFreq,
StaticHuffman.FreqListToLenList( this.offHiFreq ) );
int[] offHiCode = StaticHuffman.LenListToCodeList( offHiLen );
//------------------------------------------------------------------
// code部のハフマン符号表出力
if( 2 <= PostLh3Encoder.countNoZeroElement( this.codeFreq ) ){
this.writeCodeLenList( codeLen ); //throws IOException
}else{
this.out.writeBits( 15, 0x4210 ); //throws IOException
this.out.writeBits( 9,
PostLh3Encoder.getNoZeroElementIndex( this.codeFreq ) ); //throws IOException
}
//------------------------------------------------------------------
// offHi部のハフマン符号表出力
if( offHiLen != PostLh3Encoder.ConstOffHiLen ){
this.out.writeBit( 1 ); //throws IOException
if( 2 <= PostLh3Encoder.countNoZeroElement( this.offHiFreq ) ){
this.writeOffHiLenList( offHiLen ); //throws IOException
}else{
this.out.writeBits( 12, 0x0111 ); //throws IOException
this.out.writeBits( 7,
PostLh3Encoder.getNoZeroElementIndex( this.offHiFreq ) );//throws IOException
}
}else{
this.out.writeBit( 0 ); //throws IOException
}
//------------------------------------------------------------------
// ハフマン符号出力
this.position = 0;
this.flagBit = 0;
for( int i = 0 ; i < blockSize ; i++ ){
if( this.flagBit == 0 ){
this.flagBit = 0x80;
this.flagPos = this.position++;
}
if( 0 == ( this.buffer[ this.flagPos ] & this.flagBit ) ){
int code = this.buffer[ this.position++ ] & 0xFF;
this.out.writeBits( codeLen[ code ], codeCode[ code ] ); //throws IOException
}else{
int code = ( this.buffer[ this.position++ ] & 0xFF ) | 0x100;
int offset = ( ( this.buffer[ this.position++ ] & 0xFF ) << 8 )
| ( this.buffer[ this.position++ ] & 0xFF );
int offHi = offset >> 6;
if( code < CodeMax ){
this.out.writeBits( codeLen[ code ], codeCode[ code ] );//throws IOException
}else{
this.out.writeBits( codeLen[ CodeMax ], codeCode[ CodeMax ] );//throws IOException
this.out.writeBits( 8, code - CodeMax ); //throws IOException
}
this.out.writeBits( offHiLen[ offHi ], offHiCode[ offHi ] );//throws IOException
this.out.writeBits( 6, offset ); //throws IOException
}
this.flagBit >>= 1;
}
//------------------------------------------------------------------
// 次のブロックのための処理
for( int i = 0 ; i < this.codeFreq.length ; i++ ){
this.codeFreq[i] = 0;
}
for( int i = 0 ; i < this.offHiFreq.length ; i++ ){
this.offHiFreq[i] = 0;
}
this.blockSize = 0;
this.position = 0;
this.flagBit = 0;
}// if( 0 < this.blockSize )
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// write huffman list
//------------------------------------------------------------------
// private void writeCodeLenList( int[] codeLen )
// private void writeOffHiLenList( int[] offHiLen )
//------------------------------------------------------------------
/**
* code部のハフマン符号長のリストを符号化しながら書き出す。
*
* @param codeLen code部のハフマン符号長のリスト
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeCodeLenList( int[] codeLen ) throws IOException {
for( int i = 0 ; i < codeLen.length ; i++ ){
if( 0 < codeLen[i] ){
this.out.writeBits( 5, 0x10 | ( codeLen[i] - 1 ) ); //throws IOException
}else{
this.out.writeBit( 0 ); //throws IOException
}
}
}
/**
* OffHi部のハフマン符号長のリストを符号化しながら書き出す。
*
* @param OffHiLenList CodeFreq のハフマン符号長のリスト
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOffHiLenList( int[] offHiLen ) throws IOException {
for( int i = 0 ; i < offHiLen.length ; i++ ){
this.out.writeBits( 4, offHiLen[i] ); //throws IOException
}
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// staff of huffman encoder
//------------------------------------------------------------------
// private static int countNoZeroElement( int[] array )
// private static int getNoZeroElementIndex( int[] array )
//------------------------------------------------------------------
/**
* 配列内の 0でない要素数を得る。
*
* @param array 配列
*
* @return 配列内の 0でない要素数
*/
private static int countNoZeroElement( int[] array ){
int count = 0;
for( int i = 0 ; i < array.length ; i++ ){
if( 0 != array[i] ){
count++;
}
}
return count;
}
/**
* 配列内の 0でない最初の要素を得る。
*
* @param array 配列
*
* @return 配列内の 0でない最初の要素
* 全ての要素が0の場合は 0を返す。
*/
private static int getNoZeroElementIndex( int[] array ){
for( int i = 0 ; i < array.length ; i++ ){
if( 0 != array[i] ){
return i;
}
}
return 0;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// calc the length of encoded data
//------------------------------------------------------------------
// private static int[] createLenList()
// private static int[] getBetterOffHiLenList( int[] OffHiFreq,
// int[] OffHiLen )
//------------------------------------------------------------------
/**
* -lh3- の offHi部デコード用 ハフマン符号長リストを生成する。
*
* @return -lh3- の offHi部デコード用 ハフマン符号長リスト
*/
private static int[] createConstOffHiLen(){
final int length = PostLh3Encoder.DictionarySize >> 6;
final int[] list = { 2, 0x01, 0x01, 0x03, 0x06, 0x0D, 0x1F, 0x4E, 0 };
int[] offHiLen = new int[ length ];
int index = 0;
int len = list[ index++ ];
for( int i = 0 ; i < length ; i++ ){
while( list[index] == i ){
len++;
index++;
}
offHiLen[i] = len;
}
return offHiLen;
}
/**
* OffHiFreqから生成された ハフマン符号長のリストと
* 固定ハフマン符号長のリストを比較して、出力ビット
* 数の少ないものを得る。
*
* @param OffHiFreq offset部の上位6bitの出現頻度の表
* @param OffHiLen OffHiFreqから生成されたハフマン符
* 号長のリスト
*
* @return 出力ビット数の少ない方のハフマン符号長のリスト
*/
private static int[] getBetterOffHiLen( int[] OffHiFreq,
int[] OffHiLen ){
boolean detect = false;
for( int i = 0 ; i < OffHiLen.length ; i++ ){
if( 15 < OffHiLen[i] ){ //15 はwriteOffHiLenListで書きこめる最大のハフマン符号長を意味する。
detect = true;
}
}
if( !detect ){
int origTotal = 1;
int consTotal = 1;
if( 2 <= PostLh3Encoder.countNoZeroElement( OffHiFreq ) ){
origTotal += 4 * ( PostLh3Encoder.DictionarySize >> 6 );
}else{
origTotal += 4 * 3 + 7;
}
for( int i = 0 ; i < OffHiFreq.length ; i++ ){
origTotal += OffHiFreq[i] * OffHiLen[i];
consTotal += OffHiFreq[i] * PostLh3Encoder.ConstOffHiLen[i];
}
if( origTotal < consTotal ) return OffHiLen;
else return PostLh3Encoder.ConstOffHiLen;
}else{
return PostLh3Encoder.ConstOffHiLen;
}
}
}
//end of PostLh3Encoder.java
jp/gr/java_conf/dangan/util/lha/PostLh5Encoder.java 100644 0 0 144230 7574505600 17622 0 ustar 0 0 //start of PostLh5Encoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PostLh5Encoder.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.OutputStream;
import java.lang.Math;
import jp.gr.java_conf.dangan.io.Bits;
import jp.gr.java_conf.dangan.io.BitOutputStream;
import jp.gr.java_conf.dangan.util.lha.CompressMethod;
import jp.gr.java_conf.dangan.util.lha.StaticHuffman;
import jp.gr.java_conf.dangan.util.lha.PostLzssEncoder;
//import exceptions
import java.io.IOException;
import java.lang.NullPointerException;
import java.lang.IllegalArgumentException;
import jp.gr.java_conf.dangan.util.lha.BadHuffmanTableException;
import java.lang.Error;
/**
* -lh4-, -lh5-, -lh6-, -lh7- 圧縮用 PostLzssEncoder。
*
*
* -- revision history --
* $Log: PostLh5Encoder.java,v $
* Revision 1.4 2002/12/08 00:00:00 dangan
* [change]
* クラス名 を PostLh5EncoderCombo から PostLh5Encoder に変更。
*
* Revision 1.3 2002/12/06 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.2 2002/12/01 00:00:00 dangan
* [change]
* flush() されないかぎり
* 接続された OutputStream をflush() しないように変更。
*
* Revision 1.1 2002/12/01 00:00:00 dangan
* [bug fix]
* writeOutGroup でローカル変数 offLenFreq を使用しなければ
* ならない部分で this.offLenFreq を使用していた。
* [maintenance]
* PostLh5Encoder から受け継いだインスタンスフィールド
* buffer, codeFreq, offLenFreq 廃止
* ソース整備
*
* Revision 1.0 2002/07/31 00:00:00 dangan
* add to version control
* [improvement]
* DivideNum を導入する事によって処理するパターン数の減少を図る。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.4 $
*/
public class PostLh5Encoder implements PostLzssEncoder{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private BitOutputStream out
//------------------------------------------------------------------
/**
* -lh4-, -lh5-, -lh6-, -lh7- 形式の圧縮データの出力先の ビット出力ストリーム
*/
private BitOutputStream out;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private int DictionarySize
// private int MaxMatch
// private int Threshold
// private int DictionarySizeByteLen
//------------------------------------------------------------------
/**
* LZSSの辞書サイズ
*/
private int DictionarySize;
/**
* LZSSの最大一致長
*/
private int MaxMatch;
/**
* LZSS 圧縮/非圧縮 の閾値
*/
private int Threshold;
/**
* 辞書サイズを示すのに必要なバイト数
*/
private int DictionarySizeByteLen;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// current position
//------------------------------------------------------------------
// private int position
// private int flagBit
// private int flagPos
//------------------------------------------------------------------
/**
* this.block[ this.currentBlock ] 内の現在処理位置
*/
private int position;
/**
* flag バイト内の圧縮/非圧縮を示すフラグ
*/
private int flagBit;
/**
* this.block[ this.currentBlock ] 内の flagバイトの位置
*/
private int flagPos;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// huffman code blocks
//------------------------------------------------------------------
// private int currentBlock
// private byte[][] block
// private int[] blockSize
// private int[][] blockCodeFreq
// private int[][] blockOffLenFreq
//------------------------------------------------------------------
/**
* 現在処理中のハフマンブロックを示す。
*/
private int currentBlock;
/**
* ハフマンコード格納用バッファ群
*/
private byte[][] block;
/**
* 各ブロックの code データの数
*/
private int[] blockSize;
/**
* 該当するブロックの code 部分の頻度表を持つ頻度表群
*/
private int[][] blockCodeFreq;
/**
* 該当するブロックの offLen 部分の頻度表を持つ頻度表群
*/
private int[][] blockOffLenFreq;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// groups of huffman code blocks and patterns of groups
//------------------------------------------------------------------
// private int[][] pattern
// private int[][] group
//------------------------------------------------------------------
/**
* 全ブロックを幾つかのグループに分割するパターンの配列。
*/
private int[][] pattern;
/**
* 複数ブロックを組み合わせたグループの配列。
* this.group[0] 全ブロックを持つグループが
* this.group[1] this.group[2] には 全ブロックから各々最後と最初のブロックを欠いたグループが
* …というようにピラミッド状に構成される。
*/
private int[][] group;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private PostLh5Encoder()
// public PostLh5Encoder( OutputStream out )
// public PostLh5Encoder( OutputStream out, String method )
// public PostLh5Encoder( OutputStream out, String method,
// int BufferSize )
// public PostLh5Encoder( OutputStream out, String method,
// int BlockNum, int BlockSize, int DivideNum )
//------------------------------------------------------------------
/**
* 使用不可。
*/
private PostLh5Encoder(){ }
/**
* -lh5- 圧縮用 PostLzssEncoder を構築する。
* バッファサイズにはデフォルト値が使用される。
*
* @param out 圧縮データを受け取る OutputStream
*/
public PostLh5Encoder( OutputStream out ){
this( out, CompressMethod.LH5 );
}
/**
* -lh4-, -lh5-, -lh6-, -lh7- 圧縮用 PostLzssEncoder を構築する。
* バッファサイズにはデフォルト値が使用される。
*
* @param out 圧縮データを受け取る OutputStream
* @param method 圧縮法を示す文字列
* CompressMethod.LH4
* CompressMethod.LH5
* CompressMethod.LH6
* CompressMethod.LH7
* の何れかを指定する。
*
* @exception IllegalArgumentException
* method が上記以外の場合
*/
public PostLh5Encoder( OutputStream out,
String method ){
this( out, method, 16384 );
}
/**
* -lh4-, -lh5-, -lh6-, -lh7- 圧縮用 PostLzssEncoder を構築する。
*
* @param out 圧縮データを受け取る OutputStream
* @param method 圧縮法を示す文字列
* CompressMethod.LH4
* CompressMethod.LH5
* CompressMethod.LH6
* CompressMethod.LH7
* の何れかを指定する。
* @param BufferSize LZSS圧縮データを退避しておく
* バッファのサイズ
*
* @exception IllegalArgumentException
* (1) method が上記以外の場合
* (2) BufferSize が小さすぎる場合
* の何れか
*/
public PostLh5Encoder( OutputStream out,
String method,
int BufferSize ){
this( out, method, 1, BufferSize, 0 );
}
/**
* -lh4-, -lh5-, -lh6-, -lh7- 圧縮用 PostLzssEncoder を構築する。
* 1つが BlockSizeバイト の BlockNum 個のブロックを組み合わせて
* 最も出力ビット数の少ない構成で出力する。
* 組み合わせは 全ブロックを DivideNum + 1 個に分割して得られる
* 全パターンが試される。
*
* @param out 圧縮データを受け取る OutputStream
* @param method 圧縮法を示す文字列
* CompressMethod.LH4
* CompressMethod.LH5
* CompressMethod.LH6
* CompressMethod.LH7
* の何れかを指定する。
* @param BlockNum ブロック数
* @param BlockSize 1ブロックのバイト数
* @param DivideNum 最大分割数
*
* @exception IllegalArgumentException
* (1) CompressMethod が上記以外の場合
* (2) BlockNum が 0以下の場合
* (3) BlockSize が小さすぎる場合
* (4) DivideNum が 0未満であるか、BlockNum以上の場合
* のいずれか。
*/
public PostLh5Encoder( OutputStream out,
String method,
int BlockNum,
int BlockSize,
int DivideNum ){
if( CompressMethod.LH4.equals( method )
|| CompressMethod.LH5.equals( method )
|| CompressMethod.LH6.equals( method )
|| CompressMethod.LH7.equals( method ) ){
this.DictionarySize = CompressMethod.toDictionarySize( method );
this.MaxMatch = CompressMethod.toMaxMatch( method );
this.Threshold = CompressMethod.toThreshold( method );
this.DictionarySizeByteLen = ( Bits.len( this.DictionarySize - 1 ) + 7 ) / 8;
final int MinCapacity = ( DictionarySizeByteLen + 1 ) * 8 + 1;
if( out != null
&& 0 < BlockNum
&& 0 <= DivideNum && DivideNum < BlockNum
&& MinCapacity <= BlockSize ){
if( out instanceof BitOutputStream ){
this.out = (BitOutputStream)out;
}else{
this.out = new BitOutputStream( out );
}
this.currentBlock = 0;
this.block = new byte[ BlockNum ][];
this.blockSize = new int[ BlockNum ];
this.blockCodeFreq = new int[ BlockNum ][];
this.blockOffLenFreq = new int[ BlockNum ][];
int codeFreqSize = 256 + this.MaxMatch - this.Threshold + 1;
int offLenFreqSize = Bits.len( this.DictionarySize );
for( int i = 0 ; i < BlockNum ; i++ ){
this.block[i] = new byte[ BlockSize ];
this.blockCodeFreq[i] = new int[ codeFreqSize ];
this.blockOffLenFreq[i] = new int[ offLenFreqSize ];
}
this.group = PostLh5Encoder.createGroup( BlockNum, DivideNum );
this.pattern = PostLh5Encoder.createPattern( BlockNum, DivideNum );
this.position = 0;
this.flagBit = 0;
this.flagPos = 0;
}else if( out == null ){
throw new NullPointerException( "out" );
}else if( BlockNum <= 0 ){
throw new IllegalArgumentException( "BlockNum too small. BlockNum must be 1 or more." );
}else if( DivideNum < 0 || BlockNum <= DivideNum ){
throw new IllegalArgumentException( "DivideNum out of bounds( 0 to BlockNum - 1(" + ( BlockNum - 1 ) + ") )." );
}else{
throw new IllegalArgumentException( "BlockSize too small. BlockSize must be larger than " + MinCapacity );
}
}else if( method == null ){
throw new NullPointerException( "method" );
}else{
throw new IllegalArgumentException( "Unknown compress method. " + method );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void writeCode( int code )
// public void writeOffset( int offset )
//------------------------------------------------------------------
/**
* 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長を書きこむ。
*
* @param code 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
*/
public void writeCode( int code ) throws IOException {
int need = ( ( 0x100 <= code ? this.DictionarySizeByteLen + 1 : 1 )
+ ( this.flagBit == 0 ? 1 : 0 ) );
if( this.block[ this.currentBlock ].length - this.position < need
|| 65535 <= this.blockSize[ this.currentBlock ] ){
this.currentBlock++;
if( this.block.length <= this.currentBlock ){
this.writeOut();
}else{
this.position = 0;
}
this.flagBit = 0x80;
this.flagPos = this.position++;
this.block[ this.currentBlock ][ this.flagPos ] = 0;
}else if( this.flagBit == 0 ){
this.flagBit = 0x80;
this.flagPos = this.position++;
this.block[ this.currentBlock ][ this.flagPos ] = 0;
}
//データ格納
this.block[ this.currentBlock ][ this.position++ ] = (byte)code;
//上位1ビットをフラグとして格納
if( 0x100 <= code ){
this.block[ this.currentBlock ][ this.flagPos ] |= this.flagBit;
}
this.flagBit >>= 1;
//頻度表更新
this.blockCodeFreq[ this.currentBlock ][ code ]++;
//ブロックサイズ更新
this.blockSize[ this.currentBlock ]++;
}
/**
* LZSS で圧縮された圧縮コードのうち一致位置を書きこむ。
*
* @param offset LZSS で圧縮された圧縮コードのうち一致位置
*/
public void writeOffset( int offset ){
//データ格納
int shift = ( this.DictionarySizeByteLen - 1 ) << 3;
while( 0 <= shift ){
this.block[ this.currentBlock ][ this.position++ ] = (byte)( offset >> shift );
shift -= 8;
}
//頻度表更新
this.blockOffLenFreq[ this.currentBlock ][ Bits.len( offset ) ]++;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* この PostLzssEncoder にバッファリングされている全ての
* 8ビット単位のデータを出力先の OutputStream に出力し、
* 出力先の OutputStream を flush() する。
* このメソッドは圧縮率を変化させる。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PostLzssEncoder#flush()
* @see BitOutputStream#flush()
*/
public void flush() throws IOException {
this.writeOut();
this.out.flush();
}
/**
* この出力ストリームと、接続された出力ストリームを閉じ、
* 使用していたリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.writeOut(); //throws IOException
this.out.close(); //throws IOException
this.out = null;
this.block = null;
this.blockCodeFreq = null;
this.blockOffLenFreq = null;
this.group = null;
this.pattern = null;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* この PostLh5Encoder が扱うLZSS辞書のサイズを得る。
*
* @return この PostLh5Encoder が扱うLZSS辞書のサイズ
*/
public int getDictionarySize(){
return this.DictionarySize;
}
/**
* この PostLh5Encoder が扱うLZSSの最長一致長を得る。
*
* @return この PostLh5Encoder が扱うLZSSの最大一致長
*/
public int getMaxMatch(){
return this.MaxMatch;
}
/**
* この PostLh5Encoder が扱うLZSSの圧縮、非圧縮の閾値を得る。
*
* @return この PostLh5Encoder が扱うLZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return this.Threshold;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// write huffman code
//------------------------------------------------------------------
// private void writeOut()
// private void writeOutBestPattern()
// private void writeOutGroup( int[] group )
//------------------------------------------------------------------
/**
* バッファリングされた全てのデータを this.out に出力する。
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOut() throws IOException {
if( 1 < this.block.length ){
this.writeOutBestPattern();
}else{
this.writeOutGroup( new int[]{ 0 } );
this.currentBlock = 0;
}
this.position = 0;
this.flagBit = 0;
}
/**
* バッファリングされた全てのデータを最良の構成で this.out に出力する。
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOutBestPattern() throws IOException {
int[] bestPattern = null;
int[] groupHuffLen = new int[ this.group.length ];
//------------------------------------------------------------------
// group を出力したときの bit 数を求める。
for( int i = 0 ; i < this.group.length ; i++ ){
if( this.group != null ){
int blockSize = 0;
for( int j = 0 ; j < this.group[i].length ; j++ ){
blockSize += this.blockSize[ this.group[i][j] ];
}
if( 0 < blockSize && blockSize < 65536 ){
groupHuffLen[i] =
PostLh5Encoder.calcHuffmanCodeLength(
this.DictionarySize,
PostLh5Encoder.margeArrays( this.group[i], this.blockCodeFreq ),
PostLh5Encoder.margeArrays( this.group[i], this.blockOffLenFreq ) );
}else if( 0 == blockSize ){
groupHuffLen[i] = 0;
}else{
groupHuffLen[i] = -1;
}
}else{
groupHuffLen[i] = -1;
}
}
//------------------------------------------------------------------
// 出力 bit 数が最小となる pattern を総当りで求める。
int smallest = Integer.MAX_VALUE;
for( int i = 0 ; i < this.pattern.length ; i++ ){
int length = 0;
for( int j = 0 ; j < this.pattern[i].length ; j++ ){
if( 0 <= groupHuffLen[ this.pattern[i][j] ] ){
length += groupHuffLen[ this.pattern[i][j] ];
}else{
length = Integer.MAX_VALUE;
break;
}
}
if( length < smallest ){
bestPattern = this.pattern[i];
smallest = length;
}
}
//------------------------------------------------------------------
// 最も出力 bit 数の少ないパターンで出力
// どの パターン もブロックサイズが 65536 以上の
// グループを持つ場合はブロック単位で出力。
if( bestPattern != null ){
for( int i = 0 ; i < bestPattern.length ; i++ ){
this.writeOutGroup( this.group[ bestPattern[i] ] ); //throws IOException
}
}else{
for( int i = 0 ; i < this.block.length ; i++ ){
this.writeOutGroup( new int[]{ i } );
}
}
this.currentBlock = 0;
}
/**
* group で指定された ブロック群をハフマン符号化して this.out に出力する。
*
* @param group 出力するブロック番号を持つ配列
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOutGroup( int[] group ) throws IOException {
int[] codeFreq = PostLh5Encoder.margeArrays( group, this.blockCodeFreq );
int[] offLenFreq = PostLh5Encoder.margeArrays( group, this.blockOffLenFreq );
int blockSize = 0;
for( int i = 0 ; i < group.length ; i++ ){
blockSize += this.blockSize[ group[i] ];
}
if( 0 < blockSize ){
//------------------------------------------------------------------
// ブロックサイズ出力
this.out.writeBits( 16, blockSize );
//------------------------------------------------------------------
// ハフマン符号表生成
int[] codeLen = StaticHuffman.FreqListToLenList( codeFreq );
int[] codeCode = StaticHuffman.LenListToCodeList( codeLen );
int[] offLenLen = StaticHuffman.FreqListToLenList( offLenFreq );
int[] offLenCode = StaticHuffman.LenListToCodeList( offLenLen );
//------------------------------------------------------------------
// code 部のハフマン符号表出力
if( 2 <= PostLh5Encoder.countNoZeroElement( codeFreq ) ){
int[] codeLenFreq = PostLh5Encoder.createCodeLenFreq( codeLen );
int[] codeLenLen = StaticHuffman.FreqListToLenList( codeLenFreq );
int[] codeLenCode = StaticHuffman.LenListToCodeList( codeLenLen );
if( 2 <= PostLh5Encoder.countNoZeroElement( codeLenFreq ) ){
this.writeCodeLenLen( codeLenLen ); //throws IOException
}else{
this.out.writeBits( 5, 0 ); //throws IOException
this.out.writeBits( 5,
PostLh5Encoder.getNoZeroElementIndex( codeLenFreq ) );//throws IOException
}
this.writeCodeLen( codeLen, codeLenLen, codeLenCode ); //throws IOException
}else{
this.out.writeBits( 10, 0 ); //throws IOException
this.out.writeBits( 18,
PostLh5Encoder.getNoZeroElementIndex( codeFreq ) );//throws IOException
}
//------------------------------------------------------------------
// offLen 部のハフマン符号表出力
if( 2 <= PostLh5Encoder.countNoZeroElement( offLenFreq ) ){
this.writeOffLenLen( offLenLen ); //throws IOException
}else{
int len = Bits.len( Bits.len( this.DictionarySize ) );
this.out.writeBits( len, 0 ); //throws IOException
this.out.writeBits( len,
PostLh5Encoder.getNoZeroElementIndex( offLenFreq ) );//throws IOException
}
//------------------------------------------------------------------
// ハフマン符号出力
for( int i = 0 ; i < group.length ; i++ ){
this.position = 0;
this.flagBit = 0;
byte[] buffer = this.block[ group[i] ];
for( int j = 0 ; j < this.blockSize[ group[i] ] ; j++ ){
if( this.flagBit == 0 ){
this.flagBit = 0x80;
this.flagPos = this.position++;
}
if( 0 == ( buffer[ this.flagPos ] & this.flagBit ) ){
int code = buffer[ this.position++ ] & 0xFF;
this.out.writeBits( codeLen[ code ], codeCode[ code ] ); //throws IOException
}else{
int code = ( buffer[ this.position++ ] & 0xFF ) | 0x100;
int offset = 0;
for( int k = 0 ; k < this.DictionarySizeByteLen ; k++ ){
offset = ( offset << 8 ) | ( buffer[ this.position++ ] & 0xFF );
}
int offlen = Bits.len( offset );
this.out.writeBits( codeLen[ code ], codeCode[ code ] ); //throws IOException
this.out.writeBits( offLenLen[ offlen ], offLenCode[ offlen ] ); //throws IOException
if( 1 < offlen ) this.out.writeBits( offlen - 1, offset ); //throws IOException
}
this.flagBit >>= 1;
}
}
//------------------------------------------------------------------
// 次のブロックのための処理
for( int i = 0 ; i < group.length ; i++ ){
this.blockSize[ group[i] ] = 0;
codeFreq = this.blockCodeFreq[ group[i] ];
for( int j = 0 ; j < codeFreq.length ; j++ ){
codeFreq[j] = 0;
}
offLenFreq = this.blockOffLenFreq[ group[i] ];
for( int j = 0 ; j < offLenFreq.length ; j++ ){
offLenFreq[j] = 0;
}
}
}//if( 0 < blockSize )
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// write out huffman list
//------------------------------------------------------------------
// private void writeCodeLenLen( int[] codeLenLen )
// private void writeCodeLen( int[] codeLen,
// int[] codeLenLen, int[] codeLenCode )
// private void writeOffLenLen( int[] offLenLen )
//------------------------------------------------------------------
/**
* codeLen の ハフマン符号長のリストを書き出す。
*
* @param codeLenLen codeLenFreq のハフマン符号長のリスト
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeCodeLenLen( int[] codeLenLen ) throws IOException {
int end = codeLenLen.length;
while( 0 < end && codeLenLen[end - 1] == 0 ){
end--;
}
this.out.writeBits( 5, end ); //throws IOException
int index = 0;
while( index < end ){
int len = codeLenLen[ index++ ];
if( len <= 6 ) this.out.writeBits( 3, len ); //throws IOException
else this.out.writeBits( len - 3, ( 1 << ( len - 3 ) ) - 2 );//throws IOException
if( index == 3 ){
while( codeLenLen[index] == 0 && index < 6 ){
index++;
}
this.out.writeBits( 2, ( index - 3 ) & 0x03 ); //throws IOException
}
}
}
/**
* code 部のハフマン符号長のリストを
* ハフマンとランレングスで符号化しながら書き出す。
*
* @param codeLen codeFreq のハフマン符号長のリスト
* @param codeLenLen codeLenFreq のハフマン符号長のリスト
* @param codeLenCode codeLenFreq のハフマン符号のリスト
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeCodeLen( int[] codeLen,
int[] codeLenLen,
int[] codeLenCode ) throws IOException {
int end = codeLen.length;
while( 0 < end && codeLen[end - 1] == 0 ){
end--;
}
this.out.writeBits( 9, end ); //throws IOException
int index = 0;
while( index < end ){
int len = codeLen[ index++ ];
if( 0 < len ){
this.out.writeBits( codeLenLen[len + 2], codeLenCode[len + 2] );//throws IOException
}else{
int count = 1;
while( codeLen[ index ] == 0 && index < end ){
count++;
index++;
}
if( count <= 2 ){
for( int i = 0 ; i < count ; i++ )
this.out.writeBits(codeLenLen[0], codeLenCode[0]); //throws IOException
}else if( count <= 18 ){
this.out.writeBits( codeLenLen[1], codeLenCode[1] ); //throws IOException
this.out.writeBits( 4, count - 3 ); //throws IOException
}else if( count == 19 ){
this.out.writeBits( codeLenLen[0], codeLenCode[0] ); //throws IOException
this.out.writeBits( codeLenLen[1], codeLenCode[1] ); //throws IOException
this.out.writeBits( 4, 0x0F ); //throws IOException
}else{
this.out.writeBits( codeLenLen[2], codeLenCode[2] ); //throws IOException
this.out.writeBits( 9, count - 20 ); //throws IOException
}
}
}
}
/**
* offLen のハフマン符号長のリストを書き出す
*
* @param offLenLen offLenFreq のハフマン符号長のリスト
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOffLenLen( int[] offLenLen ) throws IOException {
int end = offLenLen.length;
while( 0 < end && offLenLen[end - 1] == 0 ){
end--;
}
int len = Bits.len( Bits.len( this.DictionarySize ) );
this.out.writeBits( len, end ); //throws IOException
int index = 0;
while( index < end ){
len = offLenLen[ index++ ];
if( len <= 6) this.out.writeBits( 3, len ); //throws IOException
else this.out.writeBits( len - 3, ( 1 << ( len - 3 ) ) - 2 );//throws IOException
}
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// staff of huffman encoder
//------------------------------------------------------------------
// private static int countNoZeroElement( int[] array )
// private static int getNoZeroElementIndex( int[] array )
// private static int[] margeArrays( int[] indexes, int[][] arrays )
// private static int[] createCodeLenFreq( int[] CodeLenList )
//------------------------------------------------------------------
/**
* 配列内の 0でない要素数を得る。
*
* @param array 配列
*
* @return 配列内の 0でない要素数
*/
private static int countNoZeroElement( int[] array ){
int count = 0;
for( int i = 0 ; i < array.length ; i++ ){
if( 0 != array[i] ){
count++;
}
}
return count;
}
/**
* 配列内の 0でない最初の要素を得る。
*
* @param array 配列
*
* @return 配列内の 0でない最初の要素
* 全ての要素が0の場合は 0を返す。
*/
private static int getNoZeroElementIndex( int[] array ){
for( int i = 0 ; i < array.length ; i++ ){
if( 0 != array[i] ){
return i;
}
}
return 0;
}
/**
* arrays の中から、indexes で指定された配列を連結する。
*
* @param indexes arrays内の走査対象の配列を示す添え字の表
* @param arrays 走査対象の配列を含んだリスト
*/
private static int[] margeArrays( int[] indexes, int[][] arrays ){
if( 1 < indexes.length ){
int[] array = new int[ arrays[0].length ];
for( int i = 0 ; i < indexes.length ; i++ ){
int[] src = arrays[ indexes[i] ];
for( int j = 0 ; j < src.length ; j++ ){
array[j] += src[j];
}
}
return array;
}else{
return arrays[ indexes[0] ];
}
}
/**
* codeLen をランレングスとハフマンで符号化するための頻度表を作成する。
* 作成する頻度表は
* codeLenFreq[0]には要素数0の要素が1つあって読み飛ばす事を指示する
* codeLenFreq[1]には要素数0の要素が3〜18あって、続く5bitのデータをみて
* その長さのデータを読み飛ばす事を指示する
* codeLenFreq[2]には要素数0の要素が20以上あって、続く9bitのデータをみて
* その長さのデータを読み飛ばす事を指示する
* という特殊な意味を持つ要素も含まれる。
* 従来の頻度は +2された位置にそれぞれ配置される。
*
* @param codeLen codeFreq のハフマン符号長のリスト
*
* @return codeLen の頻度表
*/
private static int[] createCodeLenFreq( int[] codeLen ){
int[] codeLenFreq = new int[ StaticHuffman.LimitLen + 3 ];
int end = codeLen.length;
while( 0 < end && codeLen[end - 1] == 0 ){
end--;
}
int index = 0;
while( index < end ){
int len = codeLen[ index++ ];
if( 0 < len ){
codeLenFreq[ len + 2 ]++;
}else{
int count = 1;
while( codeLen[ index ] == 0 && index < end ){
count++;
index++;
}
if( count <= 2 ){
codeLenFreq[0] += count;
}else if( count <= 18 ){
codeLenFreq[1]++;
}else if( count == 19 ){
codeLenFreq[0]++;
codeLenFreq[1]++;
}else{
codeLenFreq[2]++;
}
}
}
return codeLenFreq;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// calc the langth of encoded data
//------------------------------------------------------------------
// private static int calcHuffmanCodeLength( int DictionarySize,
// int[] CodeFreq, int[] OffLenFreq )
// private static int calcCodeLenLen( int[] codeLenLen )
// private static int calcCodeLen( int[] codeLen, int[] codeLenLen )
// private static int calcOffLenLen( int DictionarySize, int[] offLenLen )
//------------------------------------------------------------------
/**
* 指定された頻度情報でハフマン符号を
* 出力した場合のビット数を得る。
*
* @param DictionarySize LZSS辞書サイズ
* @param codeFreq コード部の頻度情報
* @param offLenFreq オフセット部の長さの頻度情報
*
* @return この頻度情報でハフマン符号を出力した場合のビット数を得る。
*/
private static int calcHuffmanCodeLength( int DictionarySize,
int[] codeFreq,
int[] offLenFreq ){
//------------------------------------------------------------------
// 初期化
int length = 0;
int[] codeLen, codeCode, offLenLen;
try{
codeLen = StaticHuffman.FreqListToLenList( codeFreq );
codeCode = StaticHuffman.LenListToCodeList( codeLen );
offLenLen = StaticHuffman.FreqListToLenList( offLenFreq );
}catch( BadHuffmanTableException exception ){ //発生しない
throw new Error( "caught the BadHuffmanTableException which should be never thrown." );
}
//------------------------------------------------------------------
// code 部のハフマン頻度表の長さを算出する。
length += 16;
if( 2 <= PostLh5Encoder.countNoZeroElement( codeFreq ) ){
int[] codeLenFreq = PostLh5Encoder.createCodeLenFreq( codeLen );
int[] codeLenLen = StaticHuffman.FreqListToLenList( codeLenFreq );
if( 2 <= PostLh5Encoder.countNoZeroElement( codeLenFreq ) ){
length += PostLh5Encoder.calcCodeLenLen( codeLenLen );
}else{
length += 5;
length += 5;
}
length += PostLh5Encoder.calcCodeLen( codeLen, codeLenLen );
}else{
length += 10;
length += 18;
}
//------------------------------------------------------------------
// offLen 部のハフマン頻度表の長さを算出する。
if( 2 <= PostLh5Encoder.countNoZeroElement( offLenFreq ) ){
length += PostLh5Encoder.calcOffLenLen( DictionarySize, offLenLen );
}else{
int len = Bits.len( Bits.len( DictionarySize ) );
length += len;
length += len;
}
//------------------------------------------------------------------
// LZSS圧縮後のデータをさらにハフマン符号化した長さを算出する。
for( int i = 0 ; i < codeFreq.length ; i++ ){
length += codeFreq[i] * codeLen[i];
}
for( int i = 0 ; i < offLenFreq.length ; i++ ){
length += offLenFreq[i] * ( offLenLen[i] + i - 1 );
}
return length;
}
/**
* 指定したハフマン符号長の表を出力した場合のビット数を得る。
*
* @param codeLenLen コード部のハフマン符号長を
* さらにハフマン符号化したものの表
*
* @return 指定したハフマン符号長の表を出力した場合のビット数
*/
private static int calcCodeLenLen( int[] codeLenLen ){
int length = 0;
int end = codeLenLen.length;
while( 0 < end && codeLenLen[end - 1] == 0 ){
end--;
}
length += 5;
int index = 0;
while( index < end ){
int len = codeLenLen[ index++ ];
if( len <= 6 ) length += len;
else length += len - 3;
if( index == 3 ){
while( codeLenLen[index] == 0 && index < 6 ){
index++;
}
length += 2;
}
}
return length;
}
/**
* 指定したハフマン符号長の表を出力した場合のビット数を得る。
*
* @param codeLen コード部のハフマン符号長の表
* @param codeLenLen コード部のハフマン符号長を
* さらにハフマン符号化したものの表
*
* @return 指定したハフマン符号長の表を出力した場合のビット数
*/
private static int calcCodeLen( int[] codeLen,
int[] codeLenLen ){
int length = 0;
int end = codeLen.length;
while( 0 < end && codeLen[end - 1] == 0 ){
end--;
}
length += 9;
int index = 0;
while( index < end ){
int len = codeLen[ index++ ];
if( 0 < len ){
length += codeLenLen[ len + 2 ];
}else{
int count = 1;
while( codeLen[ index ] == 0 && index < end ){
count++;
index++;
}
if( count <= 2 ){
for( int i = 0 ; i < count ; i++ )
length += codeLenLen[0];
}else if( count <= 18 ){
length += codeLenLen[1];
length += 4;
}else if( count == 19 ){
length += codeLenLen[0];
length += codeLenLen[1];
length += 4;
}else{
length += codeLenLen[2];
length += 9;
}
}
}
return length;
}
/**
* 指定したハフマン符号長の表を出力した場合のビット数を得る。
*
* @param DictionarySize LZSS辞書サイズ
* @param offLenLen オフセット部の長さのハフマン符号長の表
*
* @return 指定したハフマン符号長の表を出力した場合のビット数
*/
private static int calcOffLenLen( int DictionarySize,
int[] offLenLen ){
int length = 0;
int end = offLenLen.length;
while( 0 < end && offLenLen[end - 1] == 0 ){
end--;
}
length += Bits.len( Bits.len( DictionarySize ) );
int index = 0;
while( index < end ){
int len = offLenLen[ index++ ];
if( len <= 6) length += 3;
else length += len - 3;
}
return length;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// create group and pattern
//------------------------------------------------------------------
// private static int[][] createGroup( int BlockNum, int DivideNum )
// private static int[][] createPattern( int BlockNum, int DivideNum )
// private static int calcPatternNum( int BlockNum, int DivideNum )
//------------------------------------------------------------------
/**
* BlockNumのブロックを連続したブロックに
* グループ化したもののリストを返す。
*
* group = new int[]{ 0,1,2 }
*
* のような場合
* block[0] と block[1] と block[2]
* から成るグループであることを示す。
* またグループは
* group[0] は全ブロックから成るグループ、
* group[1] と group[2] はそれぞれ全ブロックから
* 最後のブロックと最初のブロックを欠いたもの、
* というように ピラミッド状に規則を持って生成され、
* createPattern はこの規則性を利用するため
* このメソッドを改変する場合は注意すること。
* また、使用しない group には null が入っているので注意すること。
*
* @param BlockNum ブロックの個数
* @param DivideNum 最大分割数
*
* @reutrn 生成されたグループのリスト
*/
private static int[][] createGroup( int BlockNum, int DivideNum ){
int[][] group = new int[ ( BlockNum + 1 ) * BlockNum / 2 ][];
if( DivideNum == 0 ){
//------------------------------------------------------------------
// 全ブロックを持つグループのみ生成
group[0] = new int[ BlockNum ];
for( int i = 0 ; i < BlockNum ; i++ ){
group[0][i] = i;
}
}else if( 2 < BlockNum && DivideNum == 1 ){
//------------------------------------------------------------------
// 同サイズのグループのうち最初のものと最後のものだけ生成。
int index = 0;
for( int size = BlockNum ; 0 < size ; size-- ){
group[ index ] = new int[ size ];
for( int i = 0 ; i < size ; i++ ){
group[index][i] = i;
}
if( size < BlockNum ){
index += BlockNum - size;
group[ index ] = new int[ size ];
for( int i = 0 ; i < size ; i++ ){
group[index][i] = i + BlockNum - size;
}
}
index++;
}
}else{
//------------------------------------------------------------------
// 全グループを生成。
int index = 0;
for( int size = BlockNum ; 0 < size ; size-- ){
for( int start = 0 ; size + start <= BlockNum ; start++ ){
group[index] = new int[ size ];
for( int i = 0 ; i < size ; i++ ){
group[index][i] = start + i;
}
index++;
}
}
}
return group;
}
/**
* BlockNumのブロックを最大 DivideNum + 1個の領域に
* 分割したときの パターンの表を生成する。
* 1つのパターンは createGroup で生成される
* グループ配列への添字の列挙で示される。
*
* pattern = new int[]{ 1,3 };
*
* のような パターンは group[1] と group[3] の間で
* 分割されたことを示す。
*
* @param BlockNum ブロックの個数
* @param DivideNum 最大分割数
*
* @return 生成されたパターンのリスト
*/
private static int[][] createPattern( int BlockNum, int DivideNum ){
int index = 0;
int patternNum = PostLh5Encoder.calcPatternNum( BlockNum, DivideNum );
int[][] pattern = new int[ patternNum ][];
for( int div = 0 ; div < Math.min( BlockNum, DivideNum + 1 ) ; div++ ){
//分割位置を保持する配列。
//配列内の値は、例えば 0の場合は Block[0] と Block[1] の間で分割することを意味する。
int[] divPos = new int[ div ];
for( int i = 0 ; i < divPos.length ; i++ ){
divPos[i] = i;
}
//同じ 分割数のパターンを生成するループ
//more は この分割数で、まだパターンが生成できる事を示す。
boolean more;
do{
pattern[index] = new int[ div + 1 ];
int start = 0;
for( int i = 0 ; i < divPos.length ; i++ ){
int len = ( divPos[i] - start ) + 1;
int num = BlockNum - len;
pattern[index][i] = ( num + 1 ) * num / 2 + start;
start += len;
}
int num = BlockNum - ( BlockNum - start );
pattern[index][divPos.length] = ( num + 1 ) * num / 2 + start;
index++;
//分割位置を移動する。分割位置を移動できれば、
//この分割数でまだ出力できるパターンがあると判断できる。
more = false;
int move = divPos.length - 1;
int range = BlockNum - 2;
while( 0 <= move && !more ){
if( divPos[move] < range ){
divPos[move]++;
if( move < divPos.length - 1 ){
for( int i = move ; i < divPos.length - 1 ; i++ )
divPos[i+1] = divPos[i] + 1;
}
more = true;
}
range = divPos[move] - 1;
move--;
}
}while( more );
}
return pattern;
}
/**
* BlockNum 個のブロックを
* 最大 DivideNum + 1 個に連続した領域に分割した場合
* 何パターンできるかを得る。
*
* @param BlockNum ブロックの個数
* @param DivideNum 分割数
*
* @return パターン数。
*/
private static int calcPatternNum( int BlockNum, int DivideNum ){
int patternNum = 0;
for( int div = 0 ; div <= DivideNum ; div++ ){
int count = ( div <= ( BlockNum / 2 ) ? div : BlockNum - 1 - div );
int numerator = 1;
for( int i = 1 ; i <= count ; i++ ){
numerator *= ( BlockNum - i );
}
int denominator = 1;
for( int i = 1 ; i <= count ; i++ ){
denominator *= i;
}
patternNum += numerator / denominator;
}
return patternNum;
}
}
//end of PostLh5Encoder.java
jp/gr/java_conf/dangan/util/lha/PostLz5Encoder.java 100644 0 0 25747 7572250400 17630 0 ustar 0 0 //start of PostLz5Encoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PostLz5Encoder.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.OutputStream;
import jp.gr.java_conf.dangan.util.lha.PostLzssEncoder;
//import exceptions
import java.io.IOException;
import java.lang.NullPointerException;
/**
* -lz5- 圧縮用 PostLzssEncoder。
*
*
* -- revision history --
* $Log: PostLz5Encoder.java,v $
* Revision 1.1 2002/12/01 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/07/31 00:00:00 dangan
* add to version control
* [bug fix]
* -lz5- の MaxMatch は 16 でなく 18 だった。
* flush() で出力できるデータを出力していなかったのを修正。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class PostLz5Encoder implements PostLzssEncoder{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private static final int DictionarySize
// private static final int MaxMatch
// private static final int Threshold
//------------------------------------------------------------------
/** 辞書サイズ */
private static final int DictionarySize = 4096;
/** 最大一致長 */
private static final int MaxMatch = 18;
/** 最小一致長 */
private static final int Threshold = 3;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// sink
//------------------------------------------------------------------
// private OutputStream out
//------------------------------------------------------------------
/**
* -lz5- 圧縮データを出力するストリーム
*/
private OutputStream out;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// buffer
//------------------------------------------------------------------
// private byte[] buf
// private int index
// private int flagIndex
// private int flagBit
//------------------------------------------------------------------
/** 圧縮データの一時格納用バッファ */
private byte[] buf;
/** buf内の現在処理位置 */
private int index;
/** buf内の Lzss圧縮、非圧縮を示すフラグの位置を示す */
private int flagIndex;
/** Lzss圧縮、非圧縮を示すフラグ */
private int flagBit;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// current position
//------------------------------------------------------------------
// private int position
//------------------------------------------------------------------
/**
* ストリーム内現在処理位置
* lha の offset から larc の offset への変換に必要
*/
private int position;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private PostLz5Encoder()
// public PostLz5Encoder( OutputStream out )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可
*/
private PostLz5Encoder(){ }
/**
* -lz5- 圧縮用 PostLzssEncoder を構築する。
*
* @param out 圧縮データを出力する出力ストリーム
*/
public PostLz5Encoder( OutputStream out ){
if( out != null ){
this.out = out;
this.position = 0;
this.buf = new byte[1024];
this.index = 0;
this.flagIndex = 0;
this.flagBit = 0x100;
}else{
throw new NullPointerException( "out" );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void writeCode( int code )
// public void writeOffset( int offset )
//------------------------------------------------------------------
/**
* 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長を書きこむ。
*
* @param code 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
*/
public void writeCode( int code ) throws IOException {
if( this.flagBit == 0x100 ){
if( this.buf.length - ( 2 * 8 + 1 ) < this.index ){
this.out.write( this.buf, 0, this.index ); //throws IOException
this.index = 0;
}
this.flagBit = 0x01;
this.flagIndex = this.index++;
this.buf[ this.flagIndex ] = 0;
}
if( code < 0x100 ){
this.buf[ this.flagIndex ] |= this.flagBit;
this.buf[ this.index++ ] = (byte)code;
this.position++;
}else{
this.buf[ this.index++ ] = (byte)code;
}
this.flagBit <<= 1;
}
/**
* LZSS で圧縮された圧縮コードのうち一致位置を書きこむ。
*
* @param offset LZSS で圧縮された圧縮コードのうち一致位置
*/
public void writeOffset( int offset ){
int pos = ( this.position - offset - 1
- PostLz5Encoder.MaxMatch )
& ( PostLz5Encoder.DictionarySize - 1 );
int matchlen = this.buf[ --this.index ] & 0x0F;
this.buf[ this.index++ ] = (byte)pos;
this.buf[ this.index++ ] = (byte)( ( ( pos >> 4 ) & 0xF0 ) | matchlen );
this.position += matchlen + this.Threshold;
}
//------------------------------------------------------------------
// method jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* この PostLzssEncoder にバッファリングされている
* 出力可能なデータを出力先の OutputStream に出力し、
* 出力先の OutputStream を flush() する。
* このメソッドは出力不可能な 最大15バイトのデータを
* バッファリングしたまま 出力しない。
* このメソッドは圧縮率を変化させない。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PostLzssEncoder#flush()
*/
public void flush() throws IOException {
if( this.flagBit == 0x100 ){
this.out.write( this.buf, 0, this.index ); //throws IOException
this.out.flush(); //throws IOException
this.index = 0;
this.flagBit = 0x01;
this.flagIndex = this.index++;
this.buf[ this.flagIndex ] = 0;
}else{
this.out.write( this.buf, 0, this.flagIndex ); //throws IOException
this.out.flush(); //throws IOException
System.arraycopy( this.buf, this.flagIndex,
this.buf, 0,
this.index - this.flagIndex );
this.index -= this.flagIndex;
this.flagIndex = 0;
}
}
/**
* この出力ストリームと、接続された出力ストリームを閉じ、
* 使用していたリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.out.write( this.buf, 0, this.index ); //throws IOException
this.out.close(); //throws IOException
this.out = null;
this.buf = null;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* -lz5-形式の LZSS辞書のサイズを得る。
*
* @return -lz5-形式の LZSS辞書のサイズ
*/
public int getDictionarySize(){
return PostLz5Encoder.DictionarySize;
}
/**
* -lz5-形式の LZSSの最長一致長を得る。
*
* @return -lz5-形式の LZSSの最長一致長
*/
public int getMaxMatch(){
return PostLz5Encoder.MaxMatch;
}
/**
* -lz5-形式の LZSSの圧縮、非圧縮の閾値を得る。
*
* @return -lz5-形式の LZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return PostLz5Encoder.Threshold;
}
}
//end of PostLz5Encoder.java
jp/gr/java_conf/dangan/util/lha/PostLzsEncoder.java 100644 0 0 23223 7572250400 17711 0 ustar 0 0 //start of PostLzsEncoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PostLzsEncoder.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.OutputStream;
import jp.gr.java_conf.dangan.io.Bits;
import jp.gr.java_conf.dangan.io.BitOutputStream;
import jp.gr.java_conf.dangan.util.lha.PostLzssEncoder;
//import exceptions
import java.io.IOException;
import java.lang.NullPointerException;
/**
* -lzs- 圧縮用 PostLzssEncoder。
*
*
* -- revision history --
* $Log: PostLzsEncoder.java,v $
* Revision 1.1 2002/12/01 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/07/31 00:00:00 dangan
* add to version control
* [bug fix]
* -lzs- の MaxMatch は 16 でなく 17 だったのを修正。
* [maintenance]
* ソース整備
* タブの廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class PostLzsEncoder implements PostLzssEncoder {
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private static final int DictionarySize
// private static final int MaxMatch
// private static final int Threshold
//------------------------------------------------------------------
/** 辞書サイズ */
private static final int DictionarySize = 2048;
/** 最大一致長 */
private static final int MaxMatch = 17;
/** 最小一致長 */
private static final int Threshold = 2;
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// length of LZSS code
//------------------------------------------------------------------
// private static final int PositionBits
// private static final int LengthBits
//------------------------------------------------------------------
/** 一致位置のビット数 */
private static final int PositionBits = Bits.len( PostLzsEncoder.DictionarySize - 1 );
/** 一致長のビット数 */
private static final int LengthBits = Bits.len( PostLzsEncoder.MaxMatch - PostLzsEncoder.Threshold );
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private BitOutputStream out
// private int position
// private int matchLength
//------------------------------------------------------------------
/**
* -lzs- 形式のデータを出力するビット出力ストリーム
*/
private BitOutputStream out;
/**
* ストリーム内現在処理位置
*/
private int position;
/**
* 現在処理中のLZSS圧縮コード
*/
private int matchLength;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private PostLzsEncoder()
// public PostLzsEncoder( OutputStream out )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private PostLzsEncoder(){ }
/**
* -lzs- 圧縮用 PostLzssEncoder を構築する。
*
* @param out -lzs- 形式の圧縮データを出力するストリーム
*/
public PostLzsEncoder( OutputStream out ){
if( out != null ){
if( out instanceof BitOutputStream ){
this.out = (BitOutputStream)out;
}else{
this.out = new BitOutputStream( out );
}
this.position = 0;
this.matchLength = 0;
}else{
throw new NullPointerException( "out" );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public void writeCode( int code )
// public void writeOffset( int offset )
//------------------------------------------------------------------
/**
* 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長を書きこむ。
*
* @param code 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
*/
public void writeCode( int code ) throws IOException {
if( code < 0x100 ){
this.out.writeBit( 1 ); //throws IOException
this.out.writeBits( 8, code ); //throws IOException
this.position++;
}else{
// close() 後の writeCode() で
// NullPointerException を投げることを期待している。
this.out.writeBit( 0 ); //throws IOException
this.matchLength = code - 0x100;
}
}
/**
* LZSS で圧縮された圧縮コードのうち一致位置を書きこむ。
*
* @param offset LZSS で圧縮された圧縮コードのうち一致位置
*/
public void writeOffset( int offset ) throws IOException {
int pos = ( this.position - offset - 1
- PostLzsEncoder.MaxMatch )
& ( PostLzsEncoder.DictionarySize - 1 );
this.position += this.matchLength + PostLzsEncoder.Threshold;
this.out.writeBits( this.PositionBits, pos ); //throws IOException
this.out.writeBits( this.LengthBits, this.matchLength ); //throws IOException
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public void flush()
// public void close()
//------------------------------------------------------------------
/**
* この PostLzssEncoder にバッファリングされている
* 全ての 8ビット単位のデータを出力先の OutputStream に出力し、
* 出力先の OutputStream を flush() する。
* このメソッドは圧縮率を変化させない。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PostLzssEncoder#flush()
* @see BitOutputStream#flush()
*/
public void flush() throws IOException {
this.out.flush(); //throws IOException
}
/**
* この出力ストリームと、接続された出力ストリームを閉じ、
* 使用していたリソースを解放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.out.close(); //throws IOException
this.out = null;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// get LZSS patameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* -lzs-形式の LZSS辞書のサイズを得る。
*
* @return -lzs-形式の LZSS辞書のサイズ
*/
public int getDictionarySize(){
return PostLzsEncoder.DictionarySize;
}
/**
* -lzs-形式の LZSSの最大一致長を得る。
*
* @return -lzs-形式の LZSSの最大一致長
*/
public int getMaxMatch(){
return PostLzsEncoder.MaxMatch;
}
/**
* -lzs-形式の LZSSの圧縮、非圧縮の閾値を得る。
*
* @return -lzs-形式の LZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return PostLzsEncoder.Threshold;
}
}
//end of PostLzsEncoder.java
jp/gr/java_conf/dangan/util/lha/PostLzssEncoder.java 100644 0 0 13447 7517637600 20115 0 ustar 0 0 //start of PostLzssEncoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PostLzssEncoder.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
//import exceptions
import java.io.IOException;
/**
* LZSS圧縮コードを処理する インターフェイス。
*
*
* -- revision history --
* $Log: PostLzssEncoder.java,v $
* Revision 1.0 2002/07/25 00:00:00 dangan
* add to version control
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public interface PostLzssEncoder{
//------------------------------------------------------------------
// original method ( on the model of java.io.OutputStream )
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public abstract void flush()
// public abstract void close()
//------------------------------------------------------------------
/**
* この PostLzssEncoder にバッファリングされている
* 出力可能なデータを出力先の OutputStream に出力し、
* 出力先の OutputStream を flush() する。
* java.io.OutputStream の メソッド flush() と似ているが、
* flush() しなかった場合と flush() した場合の出力については
* 同じであることを保証しなくて良い。
* つまりOutputStream の flush() では同じデータを出力する事を
* 期待されるような以下の二つのコードは、
* PostLzssEncoder においては 別のデータを出力をしても良い。
*
* (1)
* PostLzssEncoder out = new ImplementedPostLzssEncoder();
* out.writeCode( 0 );
* out.writeCode( 0 );
* out.writeCode( 0 );
* out.close();
*
* (2)
* PostLzssEncoder out = new ImplementedPostLzssEncoder();
* out.writeCode( 0 );
* out.flush();
* out.writeCode( 0 );
* out.flush();
* out.writeCode( 0 );
* out.close();
*
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract void flush() throws IOException;
/**
* この出力ストリームと、接続された出力ストリームを閉じ、
* 使用していたリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract void close() throws IOException;
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// write
//------------------------------------------------------------------
// public abstract void writeCode( int code )
// public abstract void writeOffset( int offset )
//------------------------------------------------------------------
/**
* 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長を書きこむ。
* 未圧縮データは 0〜255、
* LZSS圧縮コード(一致長)は 256〜510 を使用すること。
*
* @param code 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract void writeCode( int code ) throws IOException;
/**
* LZSS で圧縮された圧縮コードのうち一致位置を書きこむ。
*
* @param offset LZSS で圧縮された圧縮コードのうち一致位置
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract void writeOffset( int offset ) throws IOException;
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public abstract int getDictionarySize()
// public abstract int getMaxMatch()
// public abstract int getThreshold()
//------------------------------------------------------------------
/**
* このPostLzssEncoderが処理するLZSS辞書のサイズを得る。
*
* @param LZSS辞書のサイズ
*/
public abstract int getDictionarySize();
/**
* このPostLzssEncoderが処理する最大一致長を得る。
*
* @param 最長一致長
*/
public abstract int getMaxMatch();
/**
* このPostLzssEncoderが処理する圧縮、非圧縮の閾値を得る。
*
* @param 圧縮、非圧縮の閾値
*/
public abstract int getThreshold();
}
//end of PostLzssEncoder.java
jp/gr/java_conf/dangan/util/lha/PreLh1Decoder.java 100644 0 0 35142 7572250400 17357 0 ustar 0 0 //start of PreLh1Decoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PreLh1Decoder.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.InputStream;
import java.lang.Math;
import jp.gr.java_conf.dangan.io.Bits;
import jp.gr.java_conf.dangan.io.BitInputStream;
import jp.gr.java_conf.dangan.util.lha.StaticHuffman;
import jp.gr.java_conf.dangan.util.lha.PreLzssDecoder;
import jp.gr.java_conf.dangan.util.lha.DynamicHuffman;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.lang.NullPointerException;
import jp.gr.java_conf.dangan.io.NotEnoughBitsException;
import jp.gr.java_conf.dangan.io.BitDataBrokenException;
import jp.gr.java_conf.dangan.util.lha.BadHuffmanTableException;
/**
* -lh1- 解凍用の PreLzssDecoder。
*
*
* -- revision history --
* $Log: PreLh1Decoder.java,v $
* Revision 1.1 2002/12/01 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [bug fix]
* available の計算が甘かったのを修正。
* [maintenance]
* ソース整備
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class PreLh1Decoder implements PreLzssDecoder{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private static final int DictionarySize
// private static final int MaxMatch
// private static final int Threshold
//------------------------------------------------------------------
/** 辞書サイズ */
private static final int DictionarySize = 4096;
/** 最大一致長 */
private static final int MaxMatch = 60;
/** 最小一致長 */
private static final int Threshold = 3;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// BitInputStream in
//------------------------------------------------------------------
/**
* -lh1- の圧縮データを供給する BitInputStream
*/
BitInputStream in;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// huffman decoder
//------------------------------------------------------------------
// DynamicHuffman huffman
// int[] offHiLen
// short[] offHiTable
// int offHiTableBits
//------------------------------------------------------------------
/**
* code部復号用の動的ハフマン木
*/
DynamicHuffman huffman;
/**
* オフセット部の上位6bit復号用
* ハフマン符号長リスト。
*/
int[] offHiLen;
/**
* オフセット部の上位6bit復号用テーブル。
*/
short[] offHiTable;
/**
* オフセット部の上位6bit復号用テーブルを引くのに必要なbit数。
*/
int offHiTableBits;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// backup for mark/reset
//------------------------------------------------------------------
// DynamicHuffman markHuffman
//------------------------------------------------------------------
/** huffman のバックアップ用 */
DynamicHuffman markHuffman;
//------------------------------------------------------------------
// constructers
//------------------------------------------------------------------
// public PreLh1Decoder( InputStream in )
//------------------------------------------------------------------
/**
* -lh1- 解凍用 PreLzssDecoder を構築する。
*
* @param in -lh1- で圧縮されたデータを供給する入力ストリーム
*/
public PreLh1Decoder( InputStream in ){
if( in != null ){
if( in instanceof BitInputStream ){
this.in = (BitInputStream)in;
}else{
this.in = new BitInputStream( in );
}
this.huffman = new DynamicHuffman( 314 );
this.markHuffman = null;
this.offHiLen = PreLh1Decoder.createLenList();
try{
this.offHiTable = StaticHuffman.createTable( this.offHiLen );
}catch( BadHuffmanTableException exception ){
}
this.offHiTableBits = Bits.len( this.offHiTable.length - 1 );
}else{
throw new NullPointerException( "in" );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int readCode()
// public int readOffset()
//------------------------------------------------------------------
/**
* -lh1- で圧縮された
* 1byte のLZSS未圧縮のデータ、
* もしくは圧縮コードのうち一致長を読み込む。
*
* @return 1byte の 未圧縮のデータもしくは、
* 圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
*/
public int readCode() throws IOException {
int node = this.huffman.childNode( DynamicHuffman.ROOT );
while( 0 <= node ){
node = this.huffman.childNode( node - ( in.readBoolean() ? 1 : 0 ) );//throws EOFException,IOException
}
int code = ~node;
this.huffman.update( code );
return code;
}
/**
* -lh1- で圧縮された
* LZSS圧縮コードのうち一致位置を読み込む。
*
* @return -lh1- で圧縮された圧縮コードのうち一致位置
*
* @exception IOException 入出力エラーが発生した場合。
* @exception EOFException データが途中までしかないため
* 予期せぬ EndOfStream に到達した場合。
* @exception BitDataBrokenException
* データが途中までしかないため
* 予期せぬ EndOfStream に到達したか、
* 他の入出力エラーが発生した。
* @exception NotEnoughBitsException
* データが途中までしかないため
* 予期せぬ EndOfStream に到達したか、
* 他の入出力エラーが発生した。
*/
public int readOffset() throws IOException {
//offHiをあらわすのに最短の場合は 0 の 3bit で
//offHiTableBits は 8bitで 両者の差は 5bit。
//そのため、下位6bitを読み込む事を加味すると
//正常なデータでは peekBits が
//NotEnoughBitsException を投げることは無い。
int offHi = this.offHiTable[ this.in.peekBits( this.offHiTableBits ) ]; //throws NotEnoughBitsException IOException
this.in.skipBits( this.offHiLen[ offHi ] ); //throws IOException
return ( offHi << 6 ) | this.in.readBits( 6 ); //throws BitDataBrokenException NotEnoughBitsException IOException
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// mark / reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupported()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
* InputStream の mark() と違い、readLimit で設定した
* 限界バイト数より前にマーク位置が無効になる可能性が
* ある事に注意すること。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*
* @see PreLzssDecoder#mark(int)
*/
public void mark( int readLimit ){
this.in.mark( readLimit * 18 / 8 + 4 );
this.markHuffman = (DynamicHuffman)this.huffman.clone();
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void reset() throws IOException {
//mark()しないで reset() しようとした場合、
//readLimit を超えて reset() しようとした場合、
//接続された InputStream が markSupported() で false を返す場合は
//BitInputStream が IOException を投げる。
this.in.reset(); //throws IOException
this.huffman = (DynamicHuffman)this.markHuffman.clone();
}
/**
* 接続された入力ストリームが mark() と reset() をサポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public boolean markSupported(){
return this.in.markSupported();
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* ブロックせずに読み出すことの出来る最低バイト数を得る。
* InputStream の available() と違い、
* この最低バイト数は必ずしも保障されていない事に注意すること。
*
* @return ブロックしないで読み出せる最低バイト数。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PreLzssDecoder#available()
*/
public int available() throws IOException {
return Math.max( this.in.availableBits() / 18 - 4, 0 ); //throws IOException
}
/**
* このストリームを閉じ、使用していた全ての資源を解放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.in.close(); //throws IOException
this.in = null;
this.huffman = null;
this.markHuffman = null;
this.offHiLen = null;
this.offHiTable = null;
this.offHiTableBits = 0;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.PreLzssDecoder
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* -lh1-形式のLZSS辞書のサイズを得る。
*
* @return -lh1-形式のLZSS辞書のサイズ
*/
public int getDictionarySize(){
return PreLh1Decoder.DictionarySize;
}
/**
* -lh1-形式のLZSSの最大一致長を得る。
*
* @return -lh1-形式のLZSSの最大一致長
*/
public int getMaxMatch(){
return PreLh1Decoder.MaxMatch;
}
/**
* -lh1-形式のLZSSの圧縮、非圧縮の閾値を得る。
*
* @return -lh1-形式のLZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return PreLh1Decoder.Threshold;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// constant huffman tree
//------------------------------------------------------------------
// private static int[] createLenList()
//------------------------------------------------------------------
/**
* -lh1- の offsetデコード用StaticHuffmanの
* ハフマン符号長リストを生成する。
*
* @return -lh1- の offsetデコード用StaticHuffmanの
* ハフマン符号長リスト
*/
private static int[] createLenList(){
final int length = 64;
final int[] list = { 3, 0x01, 0x04, 0x0C, 0x18, 0x30, 0 };
int[] LenList = new int[ length ];
int index = 0;
int len = list[ index++ ];
for( int i = 0 ; i < length ; i++ ){
if( list[index] == i ){
len++;
index++;
}
LenList[i] = len;
}
return LenList;
}
}
//end of PreLh1Decoder.java
jp/gr/java_conf/dangan/util/lha/PreLh2Decoder.java 100644 0 0 36702 7573764200 17374 0 ustar 0 0 //start of PreLh2Decoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PreLh2Decoder.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.InputStream;
import java.lang.Math;
import jp.gr.java_conf.dangan.io.BitInputStream;
import jp.gr.java_conf.dangan.util.lha.PreLzssDecoder;
import jp.gr.java_conf.dangan.util.lha.DynamicHuffman;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.lang.NullPointerException;
import jp.gr.java_conf.dangan.io.BitDataBrokenException;
/**
* -lh2- 解凍用 PreLzssDecoder。
*
*
* -- revision history --
* $Log: PreLh2Decoder.java,v $
* Revision 1.1 2002/12/06 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* [bug fix]
* available() の計算が甘かったのを修正。
* [maintenance]
* ソース整備
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class PreLh2Decoder implements PreLzssDecoder{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private static final int DictionarySize
// private static final int MaxMatch
// private static final int Threshold
//------------------------------------------------------------------
/** 辞書サイズ */
private static final int DictionarySize = 8192;
/** 最大一致長 */
private static final int MaxMatch = 256;
/** 最小一致長 */
private static final int Threshold = 3;
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// private static final int CodeSize
//------------------------------------------------------------------
/**
* code部のハフマン木のサイズ
* code部がこれ以上の値を扱う場合は余計なビットを出力して補う。
*/
private static final int CodeSize = 286;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private BitInputStream
//------------------------------------------------------------------
/**
* -lh2- の圧縮データを供給する BitInputStream
*/
private BitInputStream in;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// huffman tree
//------------------------------------------------------------------
// private DynamicHuffman codeHuffman
// private DynamicHuffman offHiHuffman
//------------------------------------------------------------------
/**
* Lzss非圧縮データ 1byte か Lzss圧縮コードのうち一致長を
* 得るための 動的ハフマン木
*/
private DynamicHuffman codeHuffman;
/**
* Lzss圧縮コードの上位7bitの値を得るための動的ハフマン木
*/
private DynamicHuffman offHiHuffman;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// current position
//------------------------------------------------------------------
// private int position
// private int nextPosition
// private int matchLength
//------------------------------------------------------------------
/**
* (解凍後のデータの)現在処理位置
*/
private int position;
/**
* 次に addLeaf() すべき position
*/
private int nextPosition;
/**
* 一致長
*/
private int matchLength;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// backup for mark method
//------------------------------------------------------------------
// private DynamicHuffman markCodeHuffman
// private DynamicHuffman markOffHiHuffman
// private int markPosition
// private int markNextPosition
// private int markMatchLength
//------------------------------------------------------------------
/** codeHuffman のバックアップ用 */
private DynamicHuffman markCodeHuffman;
/** offHiHuffman のバックアップ用 */
private DynamicHuffman markOffHiHuffman;
/** position のバックアップ用 */
private int markPosition;
/** nextPosition のバックアップ用 */
private int markNextPosition;
/** matchLength のバックアップ用 */
private int markMatchLength;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private PreLh2Decoder()
// public PreLh2Decoder( InputStream in )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private PreLh2Decoder(){ }
/**
* -lh2- 解凍用 PreLzssDecoder を構築する。
*
* @param in 圧縮データを供給する入力ストリーム
*/
public PreLh2Decoder( InputStream in ){
if( in != null ){
if( in instanceof BitInputStream ){
this.in = (BitInputStream)in;
}else{
this.in = new BitInputStream( in );
}
this.codeHuffman = new DynamicHuffman( PreLh2Decoder.CodeSize );
this.offHiHuffman = new DynamicHuffman(
PreLh2Decoder.DictionarySize >> 6, 1 );
this.position = 0;
this.nextPosition = 1 << 6;
this.matchLength = 0;
}else{
throw new NullPointerException( "in" );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int readCode()
// public int readOffset()
//------------------------------------------------------------------
/**
* -lh2- で圧縮された
* 1byte のLZSS未圧縮のデータ、
* もしくは圧縮コードのうち一致長を読み込む。
*
* @return 1byte の 未圧縮のデータもしくは、
* 圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
*/
public int readCode() throws IOException {
final int CodeMax = PreLh2Decoder.CodeSize - 1;
int node = this.codeHuffman.childNode( DynamicHuffman.ROOT );
while( 0 <= node ){
node = this.codeHuffman.childNode( node - ( in.readBoolean() ? 1 : 0 ) );//throws EOFException,IOException
}
int code = ~node;
this.codeHuffman.update( code );
if( code < 0x100 ){
this.position++;
}else{
if( code == CodeMax ){
try{
code += this.in.readBits( 8 );
}catch( BitDataBrokenException exception ){
if( exception.getCause() instanceof EOFException )
throw (EOFException)exception.getCause();
}
}
this.matchLength = code - 0x100 + PreLh2Decoder.Threshold;
}
return code;
}
/**
* -lh2- で圧縮された
* LZSS圧縮コードのうち一致位置を読み込む。
*
* @return -lh2- で圧縮された圧縮コードのうち一致位置
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
*/
public int readOffset() throws IOException {
if( this.nextPosition < PreLh2Decoder.DictionarySize ){
while( this.nextPosition < this.position ){
this.offHiHuffman.addLeaf( this.nextPosition >> 6 );
this.nextPosition += 64;
if( PreLh2Decoder.DictionarySize <= this.nextPosition )
break;
}
}
this.position += this.matchLength;
int node = this.offHiHuffman.childNode( DynamicHuffman.ROOT );
while( 0 <= node ){
node = this.offHiHuffman.childNode( node - ( in.readBoolean() ? 1 : 0 ) );//throws EOFException,IOException
}
int offHi = ~node;
this.offHiHuffman.update( offHi );
return ( offHi << 6 ) | this.in.readBits( 6 );
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// mark / reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupported()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
* InputStream の mark() と違い、readLimit で設定した
* 限界バイト数より前にマーク位置が無効になる可能性が
* ある事に注意すること。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*
* @see PreLzssDecoder#available()
*/
public void mark( int readLimit ){
this.in.mark( readLimit * 18 / 8 + 4 );
this.markCodeHuffman = (DynamicHuffman)this.codeHuffman.clone();
this.markOffHiHuffman = (DynamicHuffman)this.offHiHuffman.clone();
this.markPosition = this.position;
this.markNextPosition = this.nextPosition;
this.markMatchLength = this.matchLength;
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void reset() throws IOException {
//mark()しないで reset() しようとした場合、
//readLimit を超えて reset() しようとした場合、
//接続された InputStream が markSupported() で false を返す場合は
//BitInputStream が IOException を投げる。
this.in.reset(); //throws IOException
this.codeHuffman = (DynamicHuffman)this.markCodeHuffman.clone();
this.offHiHuffman = (DynamicHuffman)this.markOffHiHuffman.clone();
this.position = this.markPosition;
this.nextPosition = this.markNextPosition;
this.matchLength = this.markMatchLength;
}
/**
* 接続された入力ストリームが mark() と reset() を
* サポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public boolean markSupported(){
return this.in.markSupported();
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* ブロックせずに読み出すことの出来る最低バイト数を得る。
* InputStream の available() と違い、
* この最低バイト数は必ずしも保障されていない事に注意すること。
*
* @return ブロックしないで読み出せる最低バイト数。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PreLzssDecoder#available()
*/
public int available() throws IOException {
return Math.max( this.in.availableBits() / 18 - 4, 0 ); //throws IOException
}
/**
* このストリームを閉じ、使用していた全ての資源を解放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.in.close(); //throws IOException
this.in = null;
this.codeHuffman = null;
this.offHiHuffman = null;
this.markCodeHuffman = null;
this.markOffHiHuffman = null;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* -lh2-形式の LZSS辞書のサイズを得る。
*
* @return -lh2-形式の LZSS辞書のサイズ
*/
public int getDictionarySize(){
return PreLh2Decoder.DictionarySize;
}
/**
* -lh2-形式の LZSSの最大一致長を得る。
*
* @return -lh2-形式の LZSSの最大一致長
*/
public int getMaxMatch(){
return PreLh2Decoder.MaxMatch;
}
/**
* -lh2-形式の LZSSの圧縮、非圧縮の閾値を得る。
*
* @return -lh2-形式の LZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return PreLh2Decoder.Threshold;
}
}
//end of PreLh2Decoder.java
jp/gr/java_conf/dangan/util/lha/PreLh3Decoder.java 100644 0 0 63460 7572250400 17365 0 ustar 0 0 //start of PreLh3Decoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PreLh3Decoder.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.InputStream;
import java.lang.Math;
import jp.gr.java_conf.dangan.io.BitInputStream;
import jp.gr.java_conf.dangan.util.lha.StaticHuffman;
import jp.gr.java_conf.dangan.util.lha.PreLzssDecoder;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.lang.NullPointerException;
import java.lang.IllegalArgumentException;
import jp.gr.java_conf.dangan.io.BitDataBrokenException;
import jp.gr.java_conf.dangan.io.NotEnoughBitsException;
import jp.gr.java_conf.dangan.util.lha.BadHuffmanTableException;
/**
* -lh3- 解凍用の PreLzssDecoder。
*
*
* -- revision history --
* $Log: PreLh3Decoder.java,v $
* Revision 1.1 2002/12/01 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [bug fix]
* available の計算が甘かったのを修正。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class PreLh3Decoder implements PreLzssDecoder {
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private static final int DictionarySize
// private static final int MaxMatch
// private static final int Threshold
//------------------------------------------------------------------
/** 辞書サイズ */
private static final int DictionarySize = 8192;
/** 最大一致長 */
private static final int MaxMatch = 256;
/** 最小一致長 */
private static final int Threshold = 3;
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// private static final int CodeSize
//------------------------------------------------------------------
/**
* code部のハフマン木のサイズ
* code部がこれ以上の値を扱う場合は余計なビットを出力して補う。
*/
private static final int CodeSize = 286;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private BitInputStream in
//------------------------------------------------------------------
/**
* -lh3- の圧縮データを供給する BitInputStream
*/
private BitInputStream in;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// huffman decoder
//------------------------------------------------------------------
// private int blockSize
// private int[] codeLen
// private short[] codeTable
// private int codeTableBits
// private short[][] codeTree
// private int[] offHiLen
// private short[] offHiTable
// private int offHiTableBits
// private short[][] offHiTree
//------------------------------------------------------------------
/**
* 現在処理中のブロックの残りサイズを示す。
*/
private int blockSize;
/**
* code 部のハフマン符号長の表
*/
private int[] codeLen;
/**
* code 部復号用のテーブル
* 正の場合は codeTree のindexを示す。
* 負の場合は code を全ビット反転したもの。
*/
private short[] codeTable;
/**
* codeTable を引くために必要なbit数。
*/
private int codeTableBits;
/**
* codeTable に収まりきらないデータの復号用の木
* 正の場合は codeTree のindexを示す。
* 負の場合は code を全ビット反転したもの。
*/
private short[][] codeTree;
/**
* offHi 部のハフマン符号長の表
*/
private int[] offHiLen;
/**
* offHi 部復号用のテーブル
* 正の場合は offHi のindexを示す。
* 負の場合は code を全ビット反転したもの。
*/
private short[] offHiTable;
/**
* offHiTable を引くために必要なbit数。
*/
private int offHiTableBits;
/**
* offHiTable に収まりきらないデータの復号用の木
* 正の場合は offHi のindexを示す。
* 負の場合は code を全ビット反転したもの。
*/
private short[][] offHiTree;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// backup for mark/reset
//------------------------------------------------------------------
// private int markBlockSize
// private int[] markCodeLen
// private short[] markCodeTable
// private short[][] markCodeTree
// private int[] markOffHiLen
// private short[] markOffHiTable
// private short[][] markOffHiTree
//------------------------------------------------------------------
/** blockSizeのバックアップ用 */
private int markBlockSize;
/** codeLen のバックアップ用 */
private int[] markCodeLen;
/** codeTable のバックアップ用 */
private short[] markCodeTable;
/** codeTree のバックアップ用 */
private short[][] markCodeTree;
/** offHiLen のバックアップ用 */
private int[] markOffHiLen;
/** offHiTable のバックアップ用 */
private short[] markOffHiTable;
/** offHiTree のバックアップ用 */
private short[][] markOffHiTree;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private PreLh3Decoder()
// public PreLh3Decoder( InputStream in )
// public PreLh3Decoder( InputStream in,
// int CodeTableBits, int OffHiTableBits )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private PreLh3Decoder(){ }
/**
* -lh3- 解凍用 PreLzssDecoder を構築する。
* テーブルサイズには デフォルト値を使用する。
*
* @param in 圧縮データを供給する入力ストリーム
*/
public PreLh3Decoder( InputStream in ){
this( in, 12, 8 );
}
/**
* -lh3- 解凍用 PreLzssDecoder を構築する。
*
* @param in 圧縮データを供給する入力ストリーム
* @param CodeTableBits code 部を復号するために使用する
* テーブルのサイズをビット長で指定する。
* 12 を指定すれば 4096 のルックアップテーブルを生成する。
* @param OffHiTableBits offHi 部を復号するために使用する
* テーブルのサイズをビット長で指定する。
* 8 を指定すれば 256 のルックアップテーブルを生成する。
*
* @exception IllegalArgumentException
* CodeTableBits, OffHiTableBits が 0以下の場合
*/
public PreLh3Decoder( InputStream in,
int CodeTableBits,
int OffHiTableBits ){
if( in != null
&& 0 < CodeTableBits
&& 0 < OffHiTableBits ){
if( in instanceof BitInputStream ){
this.in = (BitInputStream)in;
}else{
this.in = new BitInputStream( in );
}
this.blockSize = 0;
this.codeTableBits = CodeTableBits;
this.offHiTableBits = OffHiTableBits;
}else if( in == null ){
throw new NullPointerException( "in" );
}else if( CodeTableBits <= 0 ){
throw new IllegalArgumentException( "CodeTableBits too small. CodeTableBits must be larger than 1." );
}else{
throw new IllegalArgumentException( "OffHiTableBits too small. OffHiTableBits must be larger than 1." );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int readCode()
// public int readOffset()
//------------------------------------------------------------------
/**
* -lh3- で圧縮された
* 1byte のLZSS未圧縮のデータ、
* もしくは圧縮コードのうち一致長を読み込む。
*
* @return 1byte の 未圧縮のデータもしくは、
* 圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
* @exception BadHuffmanTableException
* ハフマン木を構成するための
* ハフマン符号長の表が不正なため、
* ハフマン復号器が生成できない場合
*/
public int readCode() throws IOException {
if( this.blockSize <= 0 ){
this.readBlockHead();
}
this.blockSize--;
int code;
try{
int node = this.codeTable[ this.in.peekBits( this.codeTableBits ) ];
if( node < 0 ){
code = ~node;
this.in.skipBits( this.codeLen[ code ] );
}else{
this.in.skipBits( this.codeTableBits );
do{
node = this.codeTree[ this.in.readBit() ][ node ];
}while( 0 <= node );
code = ~node;
}
}catch( NotEnoughBitsException exception ){
int avail = exception.getAvailableBits();
int bits = this.in.peekBits( avail );
bits = bits << ( this.codeTableBits - avail );
int node = this.codeTable[ bits ];
if( node < 0 ){
code = ~node;
if( this.in.skipBits( this.codeLen[code] ) < this.codeLen[code] ){
throw new EOFException();
}
}else{
this.in.skipBits( avail );
throw new EOFException();
}
}catch( ArrayIndexOutOfBoundsException exception ){
throw new EOFException();
}
final int CodeMax = PreLh3Decoder.CodeSize - 1;
if( code == CodeMax ){
code += this.in.readBits( 8 );
}
return code;
}
/**
* -lh3- で圧縮された
* LZSS圧縮コードのうち一致位置を読み込む。
*
* @return -lh3- で圧縮された圧縮コードのうち一致位置
*
* @exception IOException 入出力エラーが発生した場合
*/
public int readOffset() throws IOException {
int offHi;
try{
int node = this.offHiTable[ this.in.peekBits( this.offHiTableBits ) ];
if( node < 0 ){
offHi = ~node;
this.in.skipBits( this.offHiLen[ offHi ] );
}else{
this.in.skipBits( this.offHiTableBits );
do{
node = this.offHiTree[ this.in.readBit() ][ node ];
}while( 0 <= node );
offHi = ~node;
}
}catch( NotEnoughBitsException exception ){
int avail = exception.getAvailableBits();
int bits = this.in.peekBits( avail );
bits = bits << ( this.offHiTableBits - avail );
int node = this.offHiTable[ bits ];
if( node < 0 ){
offHi = ~node;
if( this.offHiLen[offHi] <= avail ){
this.in.skipBits( this.offHiLen[offHi] );
}else{
this.in.skipBits( avail );
throw new EOFException();
}
}else{
this.in.skipBits( avail );
throw new EOFException();
}
}catch( ArrayIndexOutOfBoundsException exception ){
throw new EOFException();
}
return ( offHi << 6 ) | this.in.readBits( 6 );
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// mark / reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupported()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
* InputStream の mark() と違い、readLimit で設定した
* 限界バイト数より前にマーク位置が無効になる可能性が
* ある事に注意すること。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*
* @see PreLzssDecoder#mark(int)
*/
public void mark( int readLimit ){
readLimit = readLimit * StaticHuffman.LimitLen / 8;
if( this.blockSize < readLimit ){
readLimit += 245;
}
this.in.mark( readLimit );
this.markBlockSize = this.blockSize;
this.markCodeLen = this.codeLen;
this.markCodeTable = this.codeTable;
this.markCodeTree = this.codeTree;
this.markOffHiLen = this.offHiLen;
this.markOffHiTable = this.offHiTable;
this.markOffHiTree = this.offHiTree;
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void reset() throws IOException {
//mark()しないで reset() しようとした場合、
//readLimit を超えて reset() しようとした場合、
//接続された InputStream が markSupported() で false を返す場合は
//BitInputStream が IOException を投げる。
this.in.reset(); //throws IOException
this.blockSize = this.markBlockSize;
this.codeLen = this.markCodeLen;
this.codeTable = this.markCodeTable;
this.codeTree = this.markCodeTree;
this.offHiLen = this.markOffHiLen;
this.offHiTable = this.markOffHiTable;
this.offHiTree = this.markOffHiTree;
}
/**
* 接続された入力ストリームが mark() と reset() を
* サポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public boolean markSupported(){
return this.in.markSupported();
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* ブロックせずに読み出すことの出来る最低バイト数を得る。
* InputStream の available() と違い、
* この最低バイト数は必ずしも保障されていない事に注意すること。
*
* @return ブロックしないで読み出せる最低バイト数。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PreLzssDecoder#available()
*/
public int available() throws IOException {
int avail = this.in.available() * 8 / StaticHuffman.LimitLen;
if( this.blockSize < avail ){
avail -= 245;
}
return Math.max( avail, 0 );
}
/**
* このストリームを閉じ、使用していた全ての資源を解放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.in.close();
this.in = null;
this.blockSize = 0;
this.codeLen = null;
this.codeTable = null;
this.codeTree = null;
this.offHiLen = null;
this.offHiTable = null;
this.offHiTree = null;
this.markBlockSize = 0;
this.markCodeLen = null;
this.markCodeTable = null;
this.markCodeTree = null;
this.markOffHiLen = null;
this.markOffHiTable = null;
this.markOffHiTree = null;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PostLzssEncoder
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* -lh3-形式の LZSS辞書のサイズを得る。
*
* @return -lh3-形式の LZSS辞書のサイズ
*/
public int getDictionarySize(){
return PreLh3Decoder.DictionarySize;
}
/**
* -lh3-形式の LZSSの最大一致長を得る。
*
* @return -lh3-形式の LZSSの最大一致長
*/
public int getMaxMatch(){
return PreLh3Decoder.MaxMatch;
}
/**
* -lh3-形式の LZSSの圧縮、非圧縮の閾値を得る。
*
* @return -lh3-形式の LZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return PreLh3Decoder.Threshold;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// read block head
//------------------------------------------------------------------
// private void readBlockHead()
// private int[] readCodeLen()
// private int[] readOffHiLen()
//------------------------------------------------------------------
/**
* ハフマンブロックの先頭にある
* ブロックサイズやハフマン符号長のリストを読み込む。
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
* @exception BadHuffmanTableException
* ハフマン木を構成するための
* ハフマン符号長の表が不正なため、
* ハフマン復号器が生成できない場合
* @exception BitDataBrokenException
* 予期せぬ原因でデータ読みこみが
* 中断されたため要求されたビット数
* のデータが得られなかった場合
*/
private void readBlockHead() throws IOException {
//ブロックサイズ読み込み
//正常なデータの場合、この部分で EndOfStream に到達する。
try{
this.blockSize = this.in.readBits( 16 ); //throws BitDataBrokenException, EOFException, IOException
}catch( BitDataBrokenException exception ){
if( exception.getCause() instanceof EOFException ){
throw (EOFException)exception.getCause();
}else{
throw exception;
}
}
//code 部の処理
this.codeLen = this.readCodeLen();
if( 1 < this.codeLen.length ){
short[][] tableAndTree =
StaticHuffman.createTableAndTree( this.codeLen, this.codeTableBits );
this.codeTable = tableAndTree[0];
this.codeTree = new short[][]{ tableAndTree[1], tableAndTree[2] };
}else{
int code = this.codeLen[0];
this.codeLen = new int[ PreLh3Decoder.CodeSize ];
this.codeTable = new short[ 1 << this.codeTableBits ];
for( int i = 0 ; i < this.codeTable.length ; i++ ){
this.codeTable[i] = ((short)~code);
}
this.codeTree = new short[][]{ new short[0], new short[0] };
}
//offHi 部の処理
this.offHiLen = this.readOffHiLen();
if( 1 < this.offHiLen.length ){
short[][] tableAndTree =
StaticHuffman.createTableAndTree( this.offHiLen, this.offHiTableBits );
this.offHiTable = tableAndTree[0];
this.offHiTree = new short[][]{ tableAndTree[1], tableAndTree[2] };
}else{
int offHi = this.offHiLen[0];
this.offHiLen = new int[ PreLh3Decoder.DictionarySize >> 6 ];
this.offHiTable = new short[ 1 << this.offHiTableBits ];
for( int i = 0 ; i < this.offHiTable.length ; i++ ){
this.offHiTable[i] = ((short)~offHi);
}
this.offHiTree = new short[][]{ new short[0], new short[0] };
}
}
/**
* code部 のハフマン符号長のリストを読みこむ。
*
* @return ハフマン符号長のリスト。
* もしくは 長さ 1 の唯一のコード
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
* @exception BitDataBrokenException
* 予期せぬ原因でデータ読みこみが
* 中断されたため要求されたビット数
* のデータが得られなかった場合
*/
private int[] readCodeLen() throws IOException {
int[] codeLen = new int[ PreLh3Decoder.CodeSize ];
for( int i = 0 ; i < codeLen.length ; i++ ){
if( this.in.readBoolean() )
codeLen[i] = this.in.readBits( 4 ) + 1;
if( i == 2 && codeLen[0] == 1 && codeLen[1] == 1 && codeLen[2] == 1 ){
return new int[]{ this.in.readBits( 9 ) };
}
}
return codeLen;
}
/**
* offHi部のハフマン符号長のリストを読みこむ
*
* @return ハフマン符号長のリスト。
* もしくは 長さ 1 の唯一のコード
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
* @exception BitDataBrokenException
* 予期せぬ原因でデータ読みこみが
* 中断されたため要求されたビット数
* のデータが得られなかった場合
*/
private int[] readOffHiLen() throws IOException {
if( this.in.readBoolean() ){
int[] offHiLen = new int[ PreLh3Decoder.DictionarySize >> 6 ];
for( int i = 0 ; i < offHiLen.length ; i++ ){
offHiLen[i] = this.in.readBits( 4 );
if( i == 2 && offHiLen[0] == 1 && offHiLen[1] == 1 && offHiLen[2] == 1 ){
return new int[]{ this.in.readBits( 7 ) };
}
}
return offHiLen;
}else{
return PreLh3Decoder.createConstOffHiLen();
}
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// constant huffman tree
//------------------------------------------------------------------
// private static int[] createConstOffHiLen()
//------------------------------------------------------------------
/**
* -lh3- の offsetデコード用StaticHuffmanの
* ハフマン符号長リストを生成する。
*
* @return -lh3- の offsetデコード用StaticHuffmanの
* ハフマン符号長リスト
*/
private static int[] createConstOffHiLen(){
final int length = PreLh3Decoder.DictionarySize >> 6;
final int[] list = { 2, 0x01, 0x01, 0x03, 0x06, 0x0D, 0x1F, 0x4E, 0 };
int[] LenList = new int[ length ];
int index = 0;
int len = list[ index++ ];
for( int i = 0 ; i < length ; i++ ){
while( list[index] == i ){
len++;
index++;
}
LenList[i] = len;
}
return LenList;
}
}
//end of PreLh3Decoder.java
jp/gr/java_conf/dangan/util/lha/PreLh5Decoder.java 100644 0 0 137055 7574505600 17420 0 ustar 0 0 //start of PreLh5Decoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PreLh5Decoder.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.InputStream;
import java.lang.Math;
import jp.gr.java_conf.dangan.io.Bits;
import jp.gr.java_conf.dangan.util.lha.CompressMethod;
import jp.gr.java_conf.dangan.util.lha.StaticHuffman;
import jp.gr.java_conf.dangan.util.lha.PreLzssDecoder;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.lang.NullPointerException;
import java.lang.IllegalArgumentException;
import jp.gr.java_conf.dangan.io.BitDataBrokenException;
import jp.gr.java_conf.dangan.io.NotEnoughBitsException;
import jp.gr.java_conf.dangan.util.lha.BadHuffmanTableException;
/**
* -lh4-, -lh5-, -lh6-, -lh7- 解凍用の PreLzssDecoder。
*
*
* -- revision history --
* $Log: PreLh5Decoder.java,v $
* Revision 1.3 2002/12/08 00:00:00 dangan
* [bug fix]
* readCode でハフマン符号読み込み途中で
* EndOfStream に達した場合に EOFException を投げていなかった。
*
* Revision 1.2 2002/12/08 00:00:00 dangan
* [change]
* クラス名 を PreLh5DecoderFast から PreLh5Decoder に変更。
*
* Revision 1.1 2002/12/06 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [maintenance]
* 最新の BitInputStream と PreLh5Decoder からソースを取り込む。
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.3 $
*/
public class PreLh5Decoder implements PreLzssDecoder{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private InputStream in
//------------------------------------------------------------------
/**
* 接続された入力ストリーム
*/
private InputStream in;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// staff of BitInputStream
//------------------------------------------------------------------
// cache
//------------------------------------------------------------------
// private byte[] cache
// private int cacheLimit
// private int cachePosition
//------------------------------------------------------------------
/**
* 速度低下抑止用バイト配列
*/
private byte[] cache;
/**
* cache 内の有効バイト数
*/
private int cacheLimit;
/**
* cache 内の現在処理位置
*/
private int cachePosition;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// staff of BitInputStream
//------------------------------------------------------------------
// bit buffer
//------------------------------------------------------------------
// private int bitBuffer
// private int bitCount
//------------------------------------------------------------------
/**
* ビットバッファ
*/
private int bitBuffer;
/**
* bitBuffer の 有効ビット数
*/
private int bitCount;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// huffman decoder
//------------------------------------------------------------------
// private int blockSize
// private int[] codeLen
// private short[] codeTable
// private int codeTableBits
// private short[][] codeTree
// private short[] offLenTable
// private int offLenTableBits
// private short[][] offLenTree
//------------------------------------------------------------------
/**
* 現在処理中のブロックの残りサイズを示す。
*/
private int blockSize;
/**
* code 部のハフマン符号長の表
*/
private int[] codeLen;
/**
* code 部復号用のテーブル
* 正の場合は codeTree のindexを示す。
* 負の場合は code を全ビット反転したもの。
*/
private short[] codeTable;
/**
* codeTable を引くために必要なbit数。
*/
private int codeTableBits;
/**
* codeTable に収まりきらないデータの復号用の木
* 正の場合は codeTree のindexを示す。
* 負の場合は code を全ビット反転したもの。
*/
private short[][] codeTree;
/**
* offLen 部のハフマン符号長の表
*/
private int[] offLenLen;
/**
* offLen 部復号用のテーブル
* 正の場合は offLenTree のindexを示す。
* 負の場合は offLen を全ビット反転したもの。
*/
private short[] offLenTable;
/**
* offLenTable を引くために必要なbit数。
*/
private int offLenTableBits;
/**
* offLenTable に収まりきらないデータの復号用の木
* 正の場合は offLenTree のindexを示す。
* 負の場合は offLen を全ビット反転したもの。
*/
private short[][] offLenTree;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private int DictionarySize
// private int MaxMatch
// private int Threshold
//------------------------------------------------------------------
/**
* LZSS 辞書サイズ
*/
private int DictionarySize;
/**
* LZSS 最長一致長
*/
private int MaxMatch;
/**
* LZSS 圧縮/非圧縮の閾値
*/
private int Threshold;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// backup for mark/reset
//------------------------------------------------------------------
// private boolean markPositionIsInCache
// private byte[] markCache
// private int markCacheLimit
// private int markCachePosition
// private int markBitBuffer
// private int markBitCount
// private int markBlockSize
// private int[] markCodeLen
// private short[] markCodeTable
// private short[][] markCodeTree
// private int[] markOffLenLen
// private short[] markOffLenTable
// private short[][] markOffLenTree
//------------------------------------------------------------------
/**
* mark位置がキャッシュの範囲内にあるかを示す。
* markされたとき true に設定され、
* 次に in から キャッシュへの読み込みが
* 行われたときに false に設定される。
*/
private boolean markPositionIsInCache;
/** cache の バックアップ用 */
private byte[] markCache;
/** cacheAvailable のバックアップ用 */
private int markCacheLimit;
/** cachePosition のバックアップ用 */
private int markCachePosition;
/** bitBuffer のバックアップ用 */
private int markBitBuffer;
/** bitCount のバックアップ用 */
private int markBitCount;
/** blockSizeのバックアップ用 */
private int markBlockSize;
/** codeLen のバックアップ用 */
private int[] markCodeLen;
/** codeTable のバックアップ用 */
private short[] markCodeTable;
/** codeTree のバックアップ用 */
private short[][] markCodeTree;
/** offLenLen のバックアップ用 */
private int[] markOffLenLen;
/** offLenTable のバックアップ用 */
private short[] markOffLenTable;
/** offLenTree のバックアップ用 */
private short[][] markOffLenTree;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private PreLh5Decoder()
// public PreLh5Decoder( InputStream in )
// public PreLh5Decoder( InputStream in, String CompressMethod )
// public PreLh5Decoder( InputStream in, String CompressMethod,
// int CodeTableBits, int OffLenTableBits )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private PreLh5Decoder(){ }
/**
* -lh5- 解凍用 PreLzssDecoder を構築する。
* テーブルサイズはデフォルト値を使用する。
*
* @param in -lh5-形式の圧縮データを供給する入力ストリーム
*/
public PreLh5Decoder( InputStream in ){
this( in, CompressMethod.LH5, 12, 8 );
}
/**
* -lh4-,-lh5-,-lh6-,-lh7- 解凍用 PreLzssDecoder を構築する。
* テーブルサイズには デフォルト値を使用する。
*
* @param in 圧縮データを供給する入力ストリーム
* @param method 圧縮法識別子
* CompressMethod.LH4
* CompressMethod.LH5
* CompressMethod.LH6
* CompressMethod.LH7
* の何れかを指定する。
*
* @exception IllegalArgumentException
* method が上記以外の場合
*/
public PreLh5Decoder( InputStream in,
String method ){
this( in, method, 12, 8 );
}
/**
* -lh4-,-lh5-,-lh6-,-lh7- 解凍用 PreLzssDecoder を構築する。
*
* @param in 圧縮データを供給する入力ストリーム
* @param method 圧縮法識別子
* CompressMethod.LH4
* CompressMethod.LH5
* CompressMethod.LH6
* CompressMethod.LH7
* の何れかを指定する。
* @param CodeTableBits code 部を復号するために使用する
* テーブルのサイズをビット長で指定する。
* 12 を指定すれば 4096 のルックアップテーブルを生成する。
* @param OffLenTableBits offLen 部を復号するために使用する
* テーブルのサイズをビット長で指定する。
* 8 を指定すれば 256 のルックアップテーブルを生成する。
*
* @exception IllegalArgumentException
* (1) method が上記以外の場合
* (2) CodeTableBits もしくは
* OffLenTableBits が 0以下の場合
* の何れか
*/
public PreLh5Decoder( InputStream in,
String method,
int CodeTableBits,
int OffLenTableBits ){
if( CompressMethod.LH4.equals( method )
|| CompressMethod.LH5.equals( method )
|| CompressMethod.LH6.equals( method )
|| CompressMethod.LH7.equals( method ) ){
this.DictionarySize = CompressMethod.toDictionarySize( method );
this.MaxMatch = CompressMethod.toMaxMatch( method );
this.Threshold = CompressMethod.toThreshold( method );
if( in != null
&& 0 < CodeTableBits
&& 0 < OffLenTableBits ){
this.in = in;
this.cache = new byte[ 1024 ];
this.cacheLimit = 0;
this.cachePosition = 0;
this.bitBuffer = 0;
this.bitCount = 0;
this.blockSize = 0;
this.codeTableBits = CodeTableBits;
this.offLenTableBits = OffLenTableBits;
this.markPositionIsInCache = false;
this.markCache = null;
this.markCacheLimit = 0;
this.markCachePosition = 0;
this.markBitBuffer = 0;
this.markBitCount = 0;
}else if( in == null ){
throw new NullPointerException( "in" );
}else if( CodeTableBits <= 0 ){
throw new IllegalArgumentException( "CodeTableBits too small. CodeTableBits must be larger than 1." );
}else{
throw new IllegalArgumentException( "OffHiTableBits too small. OffHiTableBits must be larger than 1." );
}
}else if( null == method ){
throw new NullPointerException( "method" );
}else{
throw new IllegalArgumentException( "Unknown compress method " + method );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int readCode()
// public int readOffset()
//------------------------------------------------------------------
/**
* -lh5- 系の圧縮法で圧縮された
* 1byte のLZSS未圧縮のデータ、
* もしくは圧縮コードのうち一致長を読み込む。
*
* @return 1byte の 未圧縮のデータ、
* もしくは圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
* @exception BadHuffmanTableException
* ハフマン木を構成するための
* ハフマン符号長の表が不正である場合
*/
public int readCode() throws IOException {
if( this.blockSize <= 0 ){
this.readBlockHead();
}
this.blockSize--;
if( this.bitCount < 16 ){
if( 2 <= this.cacheLimit - this.cachePosition ){
this.bitBuffer |= ( ( this.cache[ this.cachePosition++ ] & 0xFF ) << ( 24 - this.bitCount ) )
| ( ( this.cache[ this.cachePosition++ ] & 0xFF ) << ( 16 - this.bitCount ) );
this.bitCount += 16;
}else{
this.fillBitBuffer();
int node = this.codeTable[ this.bitBuffer >>> ( 32 - this.codeTableBits ) ];
if( 0 <= node ){
int bits = this.bitBuffer << this.codeTableBits;
do{
node = this.codeTree[ bits >>> 31 ][ node ];
bits <<= 1;
}while( 0 <= node );
}
int len = this.codeLen[ ~node ];
if( len <= this.bitCount ){
this.bitBuffer <<= len;
this.bitCount -= len;
return ~node;
}else{
this.bitCount = 0;
this.bitBuffer = 0;
throw new EOFException();
}
}
}
int node = this.codeTable[ this.bitBuffer >>> ( 32 - this.codeTableBits ) ];
if( 0 <= node ){
int bits = this.bitBuffer << this.codeTableBits;
do{
node = this.codeTree[ bits >>> 31 ][ node ];
bits <<= 1;
}while( 0 <= node );
}
int len = this.codeLen[ ~node ];
this.bitBuffer <<= len;
this.bitCount -= len;
return ~node;
}
/**
* -lh5- 系の圧縮法で圧縮された
* LZSS圧縮コードのうち一致位置を読み込む。
*
* @return -lh5- 系で圧縮された圧縮コードのうち一致位置
*
* @exception IOException 入出力エラーが発生した場合
*/
public int readOffset() throws IOException {
if( this.bitCount < 16 ){
if( 2 <= this.cacheLimit - this.cachePosition ){
this.bitBuffer |= ( ( this.cache[ this.cachePosition++ ] & 0xFF ) << ( 24 - this.bitCount ) )
| ( ( this.cache[ this.cachePosition++ ] & 0xFF ) << ( 16 - this.bitCount ) );
this.bitCount += 16;
}else{
this.fillBitBuffer();
}
}
int node = this.offLenTable[ this.bitBuffer >>> ( 32 - this.offLenTableBits ) ];
if( 0 <= node ){
int bits = this.bitBuffer << this.offLenTableBits;
do{
node = this.offLenTree[ bits >>> 31 ][ node ];
bits <<= 1;
}while( 0 <= node );
}
int offlen = ~node;
int len = this.offLenLen[ offlen ];
this.bitBuffer <<= len;
this.bitCount -= len;
offlen--;
if( 0 <= offlen ){
return ( 1 << offlen ) | this.readBits( offlen );
}else{
return 0;
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupported()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
* InputStream の mark() と違い、readLimit で設定した
* 限界バイト数より前にマーク位置が無効になる可能性が
* ある事に注意すること。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*
* @see PreLzssDecoder#mark(int)
*/
public void mark( int readLimit ){
//------------------------------------------------------------------
// ハフマン符号化で最悪の場合を考慮して readLimit を計算する
if( this.blockSize < readLimit ){
readLimit = readLimit * StaticHuffman.LimitLen / 8;
readLimit += 272; //block head
}else{
readLimit = readLimit * StaticHuffman.LimitLen / 8;
}
//------------------------------------------------------------------
// BitInputStream 用キャッシュの readLimit を計算する。
readLimit -= this.cacheLimit - this.cachePosition;
readLimit -= this.bitCount / 8;
readLimit += 4;
readLimit = ( readLimit + this.cache.length - 1 ) / this.cache.length
* this.cache.length;
//------------------------------------------------------------------
// mark 処理
this.in.mark( readLimit );
if( this.markCache == null ){
this.markCache = (byte[])this.cache.clone();
}else{
System.arraycopy( this.cache, 0,
this.markCache, 0,
this.cacheLimit );
}
this.markCacheLimit = this.cacheLimit;
this.markCachePosition = this.cachePosition;
this.markBitBuffer = this.bitBuffer;
this.markBitCount = this.bitCount;
this.markPositionIsInCache = true;
this.markBlockSize = this.blockSize;
this.markCodeLen = this.codeLen;
this.markCodeTable = this.codeTable;
this.markCodeTree = this.codeTree;
this.markOffLenLen = this.offLenLen;
this.markOffLenTable = this.offLenTable;
this.markOffLenTree = this.offLenTree;
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException
* (1) mark() せずに reset() しようとした場合。
* (2) 接続された入力ストリームが markSupported()で
* false を返す場合。
* (3) 接続された入力ストリームで
* 入出力エラーが発生した場合。
* の何れか。
*/
public void reset() throws IOException {
if( this.markPositionIsInCache ){
this.cachePosition = this.markCachePosition;
this.bitBuffer = this.markBitBuffer;
this.bitCount = this.markBitCount;
this.blockSize = this.markBlockSize;
this.codeLen = this.markCodeLen;
this.codeTable = this.markCodeTable;
this.codeTree = this.markCodeTree;
this.offLenLen = this.markOffLenLen;
this.offLenTable = this.markOffLenTable;
this.offLenTree = this.markOffLenTree;
}else if( !this.in.markSupported() ){
throw new IOException( "not support mark()/reset()." );
}else if( this.markCache == null ){ //この条件式は未だにマークされていないことを示す。コンストラクタで markCache が null に設定されるのを利用する。
throw new IOException( "not marked." );
}else{
//in が reset() できない場合は
//最初の行の this.in.reset() で
//IOException を投げることを期待している。
this.in.reset(); //throws IOException
System.arraycopy( this.markCache, 0,
this.cache, 0,
this.markCacheLimit );
this.cacheLimit = this.markCacheLimit;
this.cachePosition = this.markCachePosition;
this.bitBuffer = this.markBitBuffer;
this.bitCount = this.markBitCount;
this.blockSize = this.markBlockSize;
this.codeLen = this.markCodeLen;
this.codeTable = this.markCodeTable;
this.codeTree = this.markCodeTree;
this.offLenLen = this.markOffLenLen;
this.offLenTable = this.markOffLenTable;
this.offLenTree = this.markOffLenTree;
}
}
/**
* 接続された入力ストリームが mark() と reset() を
* サポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public boolean markSupported(){
return this.in.markSupported();
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* ブロックせずに読み出すことの出来る最低バイト数を得る。
* InputStream の available() と違い、
* この最低バイト数は必ずしも保障されていない事に注意すること。
*
* @return ブロックしないで読み出せる最低バイト数。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PreLzssDecoder#available()
*/
public int available() throws IOException {
int avail = ( ( this.cacheLimit - this.cachePosition )
+ this.in.available() / this.cache.length * this.cache.length );//throws IOException
avail += this.bitCount - 32;
avail = avail / StaticHuffman.LimitLen;
if( this.blockSize < avail ){
avail -= 272;
}
return Math.max( avail, 0 );
}
/**
* このストリームを閉じ、使用していた全ての資源を解放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.in.close(); //throws IOException
this.in = null;
this.cache = null;
this.cacheLimit = 0;
this.cachePosition = 0;
this.bitBuffer = 0;
this.bitCount = 0;
this.markCache = null;
this.markCacheLimit = 0;
this.markCachePosition = 0;
this.markBitBuffer = 0;
this.markBitCount = 0;
this.markPositionIsInCache = false;
this.blockSize = 0;
this.codeLen = null;
this.codeTable = null;
this.codeTree = null;
this.offLenLen = null;
this.offLenTable = null;
this.offLenTree = null;
this.markBlockSize = 0;
this.markCodeLen = null;
this.markCodeTable = null;
this.markCodeTree = null;
this.markOffLenLen = null;
this.markOffLenTable = null;
this.markOffLenTree = null;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* この PreLh5Decoder が扱うLZSS辞書のサイズを得る。
*
* @return この PreLh5Decoder が扱うLZSS辞書のサイズ
*/
public int getDictionarySize(){
return this.DictionarySize;
}
/**
* この PreLh5Decoder が扱うLZSSの最大一致長を得る。
*
* @return この PreLh5Decoder が扱うLZSSの最大一致長
*/
public int getMaxMatch(){
return this.MaxMatch;
}
/**
* この PreLh5Decoder が扱う圧縮、非圧縮の閾値を得る。
*
* @return この PreLh5Decoder が扱う圧縮、非圧縮の閾値
*/
public int getThreshold(){
return this.Threshold;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// read block head
//------------------------------------------------------------------
// private void readBlockHead()
// private int[] readCodeLenLenList()
// private int[] readCodeLenList( HuffmanDecoder decoder )
// private int[] readOffLenLenList()
//------------------------------------------------------------------
/**
* ハフマンブロックの先頭にある
* ブロックサイズやハフマン符号長のリストを読み込む。
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
* @exception BadHuffmanTableException
* ハフマン木を構成するための
* ハフマン符号長の表が不正なため、
* ハフマン復号器が生成できない場合
* @exception BitDataBrokenException
* 予期せぬ原因でデータ読みこみが
* 中断されたため要求されたビット数
* のデータが得られなかった場合
* @exception NotEnoughBitsException
* 予期せぬ原因でデータ読みこみが
* 中断されたため要求されたビット数
* のデータが得られなかった場合
*/
private void readBlockHead() throws IOException {
//ブロックサイズ読み込み
//正常なデータの場合、この部分で EndOfStream に到達する。
try{
this.blockSize = this.readBits( 16 ); //throws BitDataBrokenException, EOFException, IOException
}catch( BitDataBrokenException exception ){
if( exception.getCause() instanceof EOFException ){
throw (EOFException)exception.getCause();
}else{
throw exception;
}
}
//codeLen 部の処理
int[] codeLenLen = this.readCodeLenLen(); //throws BitDataBrokenException, EOFException, IOException
short[] codeLenTable;
if( null != codeLenLen ){
codeLenTable = StaticHuffman.createTable( codeLenLen ); //throws BadHuffmanTableException
}else{
codeLenTable = new short[]{ (short)this.readBits( 5 ) }; //throws BitDataBrokenException EOFException IOException
codeLenLen = new int[ codeLenTable[0] + 1 ];
}
//code 部の処理
this.codeLen = this.readCodeLen( codeLenTable, codeLenLen ); //throws BitDataBrokenException NotEnoughBitsException EOFException IOException
if( null != this.codeLen ){
short[][] tableAndTree =
StaticHuffman.createTableAndTree( this.codeLen, this.codeTableBits );//throws BadHuffmanTableException
this.codeTable = tableAndTree[0];
this.codeTree = new short[][]{ tableAndTree[1], tableAndTree[2] };
}else{
int code = this.readBits( 9 ); //throws BitDataBrokenException EOFException IOException
this.codeLen = new int[ 256 + this.MaxMatch - this.Threshold + 1 ];
this.codeTable = new short[ 1 << this.codeTableBits ];
for( int i = 0 ; i < this.codeTable.length ; i++ ){
this.codeTable[i] = ((short)~code);
}
this.codeTree = new short[][]{ new short[0], new short[0] };
}
//offLen 部の処理
this.offLenLen = this.readOffLenLen(); //throws BitDataBrokenException EOFException IOException
if( null != this.offLenLen ){
short[][] tableAndTree =
StaticHuffman.createTableAndTree( this.offLenLen, this.offLenTableBits );//throws BadHuffmanTableException
this.offLenTable = tableAndTree[0];
this.offLenTree = new short[][]{ tableAndTree[1], tableAndTree[2] };
}else{
int offLen = this.readBits( Bits.len( Bits.len( this.DictionarySize ) ) );//throws BitDataBrokenException EOFException IOException
this.offLenLen = new int[ Bits.len( this.DictionarySize ) ];
this.offLenTable = new short[ 1 << this.offLenTableBits ];
for( int i = 0 ; i < this.offLenTable.length ; i++ ){
this.offLenTable[i] = ((short)~offLen);
}
this.offLenTree = new short[][]{ new short[0], new short[0] };
}
}
/**
* Codeのハフマン符号長のリストの
* ハフマン符号を復号するための
* ハフマン符号長のリストを読みこむ。
*
* @return ハフマン符号長のリスト。
* 符号長のリストが無い場合は null
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
* @exception BitDataBrokenException
* 予期せぬ原因でデータ読みこみが
* 中断されたため要求されたビット数
* のデータが得られなかった場合
*/
private int[] readCodeLenLen() throws IOException {
int listlen = this.readBits( 5 ); //throws BitDataBrokenException, EOFException, IOException
if( 0 < listlen ){
int[] codeLenLen = new int[listlen];
int index = 0;
while( index < listlen ){
int codelenlen = this.readBits( 3 ); //throws BitDataBrokenException, EOFException, IOException
if( codelenlen == 0x07 ){
while( this.readBoolean() ) codelenlen++; //throws EOFException, IOException
}
codeLenLen[index++] = codelenlen;
if( index == 3 ){
index += this.readBits( 2 ); //throws BitDataBrokenException, EOFException, IOException
}
}
return codeLenLen;
}else{
return null;
}
}
/**
* Codeのハフマン符号長のリストを復号しながら読みこむ
*
* @return ハフマン符号長のリスト。
* 符号長のリストが無い場合は null
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
* @exception BitDataBrokenException
* 予期せぬ原因でデータ読みこみが
* 中断されたため要求されたビット数
* のデータが得られなかった場合
* @exception NotEnouthBitsException
* 予期せぬ原因でデータ読みこみが
* 中断されたため要求されたビット数
* のデータが得られなかった場合
*/
private int[] readCodeLen( short[] codeLenTable, int[] codeLenLen )
throws IOException {
final int codeLenTableBits = Bits.len( codeLenTable.length - 1 );
int listlen = this.readBits( 9 ); //throws BitDataBrokenException, EOFException, IOException
if( 0 < listlen ){
int[] codeLen = new int[listlen];
int index = 0;
while( index < listlen ){
this.fillBitBuffer();
int bits = ( 0 < codeLenTableBits
? this.bitBuffer >>> ( 32 - codeLenTableBits )
: 0 );
int codelen = codeLenTable[ bits ];
int len = codeLenLen[ codelen ];
this.bitBuffer <<= len;
this.bitCount -= len;
if( codelen == 0 ) index++;
else if( codelen == 1 ) index += this.readBits( 4 ) + 3; //throws BitDataBrokenException, EOFException, IOException
else if( codelen == 2 ) index += this.readBits( 9 ) + 20; //throws BitDataBrokenException, EOFException, IOException
else codeLen[index++] = codelen - 2;
}
return codeLen;
}else{
return null;
}
}
/**
* offLen のハフマン符号長のリストを読みこむ
*
* @return ハフマン符号長のリスト。
* 符号長のリストが無い場合は null
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
* @exception BitDataBrokenException
* 予期せぬ原因でデータ読みこみが
* 中断されたため要求されたビット数
* のデータが得られなかった場合
*/
private int[] readOffLenLen() throws IOException {
int listlen = this.readBits( Bits.len( Bits.len( this.DictionarySize ) ) );//throws BitDataBrokenException, EOFException, IOException
if( 0 < listlen ){
int[] offLenLen = new int[listlen];
int index = 0;
while( index < listlen ){
int offlenlen = this.readBits( 3 ); //throws BitDataBrokenException, EOFException, IOException
if( offlenlen == 0x07 ){
while( this.readBoolean() ) offlenlen++; //throws EOFException, IOException
}
offLenLen[index++] = offlenlen;
}
return offLenLen;
}else{
return null;
}
}
//------------------------------------------------------------------
// staff of BitInputStream
//------------------------------------------------------------------
// bit read
//------------------------------------------------------------------
// private boolean readBoolean()
// private int readBits( int count )
// private int cachedBits()
//------------------------------------------------------------------
/**
* 接続された入力ストリームから 1ビットのデータを
* 真偽値として読み込む。
*
* @return 読み込まれた1ビットのデータが
* 1であれば true、0であれば false を返す。
*
* @exception EOFException 既にEndOfStreamに達していた場合
* @exception IOException 接続された入力ストリームで
* 入出力エラーが発生した場合
*/
private boolean readBoolean() throws IOException {
if( 0 < this.bitCount ){
boolean bool = ( this.bitBuffer < 0 );
this.bitBuffer <<= 1;
this.bitCount -= 1;
return bool;
}else{
this.fillBitBuffer();
boolean bool = ( this.bitBuffer < 0 );
this.bitBuffer <<= 1;
this.bitCount -= 1;
return bool;
}
}
/**
* 接続された入力ストリームから count ビットのデータを
* 読み込む。 戻り値が int値である事からも判るように
* 読み込むことのできる 最大有効ビット数は 32ビットで
* あるが、count は32以上の値を設定してもチェックを
* 受けないため それ以上の値を設定した場合は ビット
* データが読み捨てられる。
* たとえば readBits( 33 ) としたときは まず1ビットの
* データを読み捨て、その後の 32ビットのデータを返す。
* また count に 0以下の数字を設定して呼び出した場合、
* データを読み込む動作を伴わないため 戻り値は 常に0、
* EndOfStream に達していても EOFException を
* 投げない点に注意すること。
*
* @param count 読み込むデータのビット数
*
* @return 読み込まれたビットデータ。
*
* @exception IOException
* 接続された入力ストリームで
* 入出力エラーが発生した場合
* @exception EOFException
* 既にEndOfStreamに達していた場合
* @exception BitDataBrokenException
* 読み込み途中で EndOfStreamに達したため
* 要求されたビット数のデータの読み込み
* に失敗した場合。
*/
private int readBits( int count ) throws IOException {
if( 0 < count ){
if( count <= this.bitCount ){
int bits = this.bitBuffer >>> ( 32 - count );
this.bitBuffer <<= count;
this.bitCount -= count;
return bits;
}else{
final int requested = count;
int bits = 0;
try{
this.fillBitBuffer(); //throws LocalEOFException IOException
while( this.bitCount < count ){
count -= this.bitCount;
if( count < 32 ){
bits |= ( this.bitBuffer >>> ( 32 - this.bitCount ) ) << count;
}
this.bitBuffer = 0;
this.bitCount = 0;
this.fillBitBuffer(); //throws LocalEOFException IOException
}
bits |= this.bitBuffer >>> ( 32 - count );
this.bitBuffer <<= count;
this.bitCount -= count;
return bits;
}catch( LocalEOFException exception ){
if( exception.thrownBy( this ) && count < requested ){
throw new BitDataBrokenException( exception, bits >>> count, requested - count );
}else{
throw exception;
}
}
}
}else{
return 0;
}
}
/**
* この BitInputStream 内に蓄えられているビット数を得る。
*
* @return この BitInputStream 内に蓄えられているビット数。
*/
private int cachedBits(){
return this.bitCount + ( ( this.cacheLimit - this.cachePosition ) << 3 );
}
//------------------------------------------------------------------
// staff of BitInputSteram
//------------------------------------------------------------------
// fill
//------------------------------------------------------------------
// private void fillBitBuffer()
// private void fillCache()
//------------------------------------------------------------------
/**
* bitBuffer にデータを満たす。
* EndOfStream 付近を除いて bitBuffer には
* 25bit のデータが確保されることを保障する。
*
* @exception IOException 入出力エラーが発生した場合
* @exception LocalEOFException bitBuffer が空の状態で EndOfStream に達した場合
*/
private void fillBitBuffer() throws IOException {
if( 32 <= this.cachedBits() ){
if( this.bitCount <= 24 ){
if( this.bitCount <= 16 ){
if( this.bitCount <= 8 ){
if( this.bitCount <= 0 ){
this.bitBuffer = this.cache[this.cachePosition++] << 24;
this.bitCount = 8;
}
this.bitBuffer |= ( this.cache[this.cachePosition++] & 0xFF )
<< ( 24 - this.bitCount );
this.bitCount += 8;
}
this.bitBuffer |= ( this.cache[this.cachePosition++] & 0xFF )
<< ( 24 - this.bitCount );
this.bitCount += 8;
}
this.bitBuffer |= ( this.cache[this.cachePosition++] & 0xFF )
<< ( 24 - this.bitCount );
this.bitCount += 8;
}
}else if( this.bitCount < 25 ){
if( this.bitCount == 0 ){
this.bitBuffer = 0;
}
int count = Math.min( ( 32 - this.bitCount ) >> 3,
this.cacheLimit - this.cachePosition );
while( 0 < count-- ){
this.bitBuffer |= ( this.cache[this.cachePosition++] & 0xFF )
<< ( 24 - this.bitCount );
this.bitCount += 8;
}
this.fillCache(); //throws IOException
if( this.cachePosition < this.cacheLimit ){
count = Math.min( ( 32 - this.bitCount ) >> 3,
this.cacheLimit - this.cachePosition );
while( 0 < count-- ){
this.bitBuffer |= ( this.cache[this.cachePosition++] & 0xFF )
<< ( 24 - this.bitCount );
this.bitCount += 8;
}
}else if( this.bitCount <= 0 ){
throw new LocalEOFException( this );
}
}
}
/**
* cache が空になった時に cache にデータを読み込む。
*
* @exception IOException 入出力エラーが発生した場合
*/
private void fillCache() throws IOException {
this.markPositionIsInCache = false;
this.cacheLimit = 0;
this.cachePosition = 0;
//cache にデータを読み込む
int read = 0;
while( 0 <= read && this.cacheLimit < this.cache.length ){
read = this.in.read( this.cache,
this.cacheLimit,
this.cache.length - this.cacheLimit ); //throws IOException
if( 0 < read ) this.cacheLimit += read;
}
}
//------------------------------------------------------------------
// inner classes
//------------------------------------------------------------------
// private static class LocalEOFException
//------------------------------------------------------------------
/**
* BitInputStream 内で EndOfStream の検出に
* EOFException を使用するのは少々問題があるので
* ローカルな EOFException を定義する。
*/
private static class LocalEOFException extends EOFException {
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private Object owner
//------------------------------------------------------------------
/**
* この例外を投げたオブジェクト
*/
private Object owner;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public LocalEOFException()
// public LocalEOFException( String message )
//------------------------------------------------------------------
/**
* コンストラクタ。
*
* @param object この例外を投げたオブジェクト
*/
public LocalEOFException( Object object ){
super();
this.owner = object;
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// public boolean thrownBy( Object object )
//------------------------------------------------------------------
/**
* この例外が object によって投げられたかどうかを得る。
*
* @param object オブジェクト
*
* @return この例外が objectによって
* 投げられた例外であれば true
* 違えば false
*/
public boolean thrownBy( Object object ){
return this.owner == object;
}
}
}
//end of PreLh5Decoder.java
jp/gr/java_conf/dangan/util/lha/PreLz5Decoder.java 100644 0 0 34046 7572250400 17407 0 ustar 0 0 //start of PreLz5Decoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PreLz5Decoder.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.InputStream;
import java.lang.Math;
import jp.gr.java_conf.dangan.io.CachedInputStream;
import jp.gr.java_conf.dangan.util.lha.PreLzssDecoder;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.lang.NullPointerException;
/**
* -lz5- 解凍用 PreLzssDecoder。
*
*
* -- revision history --
* $Log: PreLz5Decoder.java,v $
* Revision 1.1 2002/12/01 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class PreLz5Decoder implements PreLzssDecoder{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private static final int DictionarySize
// private static final int MaxMatch
// private static final int Threshold
//------------------------------------------------------------------
/** 辞書サイズ */
private static final int DictionarySize = 4096;
/** 最大一致長 */
private static final int MaxMatch = 18;
/** 最小一致長 */
private static final int Threshold = 3;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private InputStream in
//------------------------------------------------------------------
/**
* -lz5- 形式の圧縮データを供給するストリーム
*/
private InputStream in;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// current position
//------------------------------------------------------------------
// private int position
// private int matchPos
// private int matchLen
//------------------------------------------------------------------
/**
* 現在処理位置。
* larc の一致位置から lha の一致位置への変換に必要
*/
private int position;
/** Lzss圧縮情報のうち 一致位置(larcの一致位置) */
private int matchPos;
/** Lzss圧縮符号のうち 一致長 */
private int matchLen;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// flag
//------------------------------------------------------------------
// private int flagByte
// private int flagBit
//------------------------------------------------------------------
/** 8つのLzss圧縮、非圧縮を示すフラグをまとめたもの */
private int flagByte;
/** Lzss圧縮、非圧縮を示すフラグ */
private int flagBit;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// backup for mark/reset
//------------------------------------------------------------------
// private int markPosition
// private int markMatchPos
// private int markMatchLen
// private int markFlagByte
// private int markFlagBit
//------------------------------------------------------------------
/** positionのバックアップ用 */
private int markPosition;
/** matchOffsetのバックアップ用 */
private int markMatchPos;
/** matchLengthのバックアップ用 */
private int markMatchLen;
/** flagByteのバックアップ用。*/
private int markFlagByte;
/** flagCountのバックアップ用。 */
private int markFlagBit;
//------------------------------------------------------------------
// constructer
//------------------------------------------------------------------
// private PreLz5Decoder()
// public PreLz5Decoder( InputStream in )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可
*/
private PreLz5Decoder(){ }
/**
* -lz5- 解凍用 PreLzssDecoder を構築する。
*
* @param in 圧縮データを供給する入力ストリーム
*/
public PreLz5Decoder( InputStream in ){
if( in != null ){
if( in instanceof CachedInputStream ){
this.in = (CachedInputStream)in;
}else{
this.in = new CachedInputStream( in );
}
this.position = 0;
this.matchPos = 0;
this.matchLen = 0;
this.flagByte = 0;
this.flagBit = 0x100;
this.markPosition = 0;
this.markMatchPos = 0;
this.markMatchLen = 0;
this.markFlagByte = 0;
this.markFlagBit = 0;
}else{
throw new NullPointerException( "in" );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.PreLzssDecoder
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int readCode()
// public int readOffset()
//------------------------------------------------------------------
/**
* -lz5- で圧縮された
* 1byte の LZSS未圧縮のデータ、
* もしくは圧縮コードのうち一致長を読み込む。
*
* @return 1byte の 未圧縮のデータもしくは、
* 圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
*/
public int readCode() throws IOException {
if( this.flagBit == 0x100 ){
this.flagByte = this.in.read(); //throws IOException
if( 0 <= this.flagByte ){
this.flagBit = 0x01;
}else{
throw new EOFException();
}
}
if( 0 != ( this.flagByte & this.flagBit ) ){
this.flagBit <<= 1;
this.position++;
int ret = this.in.read(); //throws IOException
if( 0 <= ret ) return ret;
else throw new EOFException();
}else{
this.flagBit <<= 1;
int c1 = this.in.read(); //throws IOException
int c2 = this.in.read(); //throws IOException
if( 0 <= c1 ){
this.matchPos = ( ( c2 & 0xF0 ) << 4 ) | c1;
this.matchLen = c2 & 0x0F;
return this.matchLen | 0x100;
}else{
throw new EOFException();
}
}
}
/**
* -lz5- で圧縮された
* 圧縮コードのうち一致位置を読み込む。
*
* @return -lz5- で圧縮された圧縮コードのうち一致位置
*
* @exception IOException 入出力エラーが発生した場合
*/
public int readOffset() throws IOException {
int offset = ( this.position - this.matchPos - 1
- PreLz5Decoder.MaxMatch )
& ( PreLz5Decoder.DictionarySize - 1 );
this.position += this.matchLen + PreLz5Decoder.Threshold;
return offset;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.PreLzssDecoder
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupported()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*
* @see PreLzssDecoder#mark(int)
*/
public void mark( int readLimit ){
this.in.mark( ( readLimit * 9 + 7 ) / 8 + 2 );
this.markPosition = this.position;
this.markMatchLen = this.matchLen;
this.markMatchPos = this.matchPos;
this.markFlagByte = this.flagByte;
this.markFlagBit = this.flagBit;
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException
* (1) mark() せずに reset() しようとした場合。
* (2) 接続された入力ストリームが markSupported()で
* false を返す場合。
* (3) 接続された入力ストリームで
* 入出力エラーが発生した場合。
* の何れか。
*/
public void reset() throws IOException {
//mark() していないのに reset() しようとした場合、
//接続されたストリームがmark/resetをサポートしない場合は
//CachedInputStream が IOException を投げる。
this.in.reset(); //throws IOException
this.position = this.markPosition;
this.matchLen = this.markMatchLen;
this.matchPos = this.markMatchPos;
this.flagByte = this.markFlagByte;
this.flagBit = this.markFlagBit;
}
/**
* 接続されたストリームが mark() と reset()
* をサポートするかを返す。
*
* @return 接続されたストリームが mark,reset をサポートするならtrue,
* サポートしないなら false
*/
public boolean markSupported(){
return this.in.markSupported();
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.PreLzssDecoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* ブロックせずに読み出すことの出来る最低バイト数を得る。
* この値は保証される。
*
* @return ブロックしないで読み出せる最低バイト数。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PreLzssDecoder#available()
*/
public int available() throws IOException {
return Math.max( in.available() * 8 / 9 - 2, 0 ); //throws IOException
}
/**
* このストリームを閉じ、使用していた全ての資源を解放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.in.close(); //throws IOException
this.in = null;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.PreLzssDecoder
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* -lz5-形式の LZSS辞書のサイズを得る。
*
* @return -lz5-形式の LZSS辞書のサイズ
*/
public int getDictionarySize(){
return PreLz5Decoder.DictionarySize;
}
/**
* -lz5-形式の LZSSの最大一致長を得る。
*
* @return -lz5-形式の LZSSの最大一致長
*/
public int getMaxMatch(){
return PreLz5Decoder.MaxMatch;
}
/**
* -lz5-形式の LZSSの圧縮、非圧縮の閾値を得る。
*
* @return -lz5-形式の LZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return PreLz5Decoder.Threshold;
}
}
//end of PreLz5Decoder.java
jp/gr/java_conf/dangan/util/lha/PreLzsDecoder.java 100644 0 0 32733 7573764200 17517 0 ustar 0 0 //start of PreLzsDecoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PreLzsDecoder.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import java.io.InputStream;
import jp.gr.java_conf.dangan.io.Bits;
import jp.gr.java_conf.dangan.io.BitInputStream;
import jp.gr.java_conf.dangan.util.lha.PreLzssDecoder;
//import exceptions
import java.io.IOException;
import java.io.EOFException;
import java.lang.NullPointerException;
import jp.gr.java_conf.dangan.io.BitDataBrokenException;
/**
* -lzs- 解凍用 PreLzssDecoder。
*
*
* -- revision history --
* $Log: PreLzsDecoder.java,v $
* Revision 1.1 2002/12/06 00:00:00 dangan
* [maintenance]
* ソース整備
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [bug fix]
* -lzs- の MaxMatch が 17 であるべきが 16 となっていたのを修正。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class PreLzsDecoder implements PreLzssDecoder{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private static final int DictionarySize
// private static final int MaxMatch
// private static final int Threshold
//------------------------------------------------------------------
/** 辞書サイズ */
private static final int DictionarySize = 2048;
/** 最大一致長 */
private static final int MaxMatch = 17;
/** 最小一致長 */
private static final int Threshold = 2;
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// bit length
//------------------------------------------------------------------
// private static final int OffsetBits
// private static final int LengthBits
//------------------------------------------------------------------
/** 一致位置のビット数 */
private static final int OffsetBits = Bits.len( PreLzsDecoder.DictionarySize - 1 );
/** 一致長のビット数 */
private static final int LengthBits = Bits.len( PreLzsDecoder.MaxMatch - PreLzsDecoder.Threshold );
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// source
//------------------------------------------------------------------
// private BitInputStream in
//------------------------------------------------------------------
/**
* -lzs- 形式の圧縮データを供給する BitInputStream
*/
private BitInputStream in;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// current position
//------------------------------------------------------------------
// private int position
// private int matchOffset
// private int matchLength
//------------------------------------------------------------------
/**
* 現在処理位置。
* LzssInputStreamの内部状態を取得できないために存在する。
* LzssInputStreamの内部クラスとして書けば、positionは必要無い。
*/
private int position;
/** 最も新しいLzssコードの一致位置 */
private int matchOffset;
/**
* 最も新しいLzssコードの一致長
* LzssInputStreamの内部状態を取得できないために存在する。
* LzssInputStreamの内部クラスとして書けば、matchLengthは必要無い。
*/
private int matchLength;
//------------------------------------------------------------------
// member values
//------------------------------------------------------------------
// backup for mark/reset
//------------------------------------------------------------------
// private int markPosition
// private int markMatchOffset
// private int markMatchLength
//------------------------------------------------------------------
/** matchPositionのバックアップ用 */
private int markPosition;
/** matchPositionのバックアップ用 */
private int markMatchOffset;
/** matchLengthのバックアップ用 */
private int markMatchLength;
//------------------------------------------------------------------
// constructers
//------------------------------------------------------------------
// public PreLzsDecoder( InputStream in )
//------------------------------------------------------------------
/**
* -lzs- 解凍用 PreLzssDecoder を構築する。
*
* @param in -lzs- 形式の圧縮データを供給する入力ストリーム
*/
public PreLzsDecoder( InputStream in ){
if( in != null ){
if( in instanceof BitInputStream ){
this.in = (BitInputStream)in;
}else{
this.in = new BitInputStream( in );
}
this.position = 0;
this.matchOffset = 0;
this.matchLength = 0;
}else{
throw new NullPointerException( "in" );
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public int readCode()
// public int readOffset()
//------------------------------------------------------------------
/**
* -lzs- で圧縮された
* 1byte の LZSS未圧縮のデータ、
* もしくは圧縮コードのうち一致長を読み込む。
*
* @return 1byte の 未圧縮のデータもしくは、
* 圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
*/
public int readCode() throws IOException {
try{
if( this.in.readBoolean() ){
this.position++;
return this.in.readBits( 8 );
}else{
this.matchOffset = this.in.readBits( this.OffsetBits );
this.matchLength = this.in.readBits( this.LengthBits );
return this.matchLength | 0x100;
}
}catch( BitDataBrokenException exception ){
if( exception.getCause() instanceof EOFException )
throw (EOFException)exception.getCause();
else
throw exception;
}
}
/**
* -lzs- で圧縮された圧縮コードのうち
* 一致位置を読み込む。
*
* @return -lzs- で圧縮された圧縮コードのうち一致位置
*
* @exception IOException 入出力エラーが発生した場合
*/
public int readOffset() throws IOException {
int offset = ( this.position - this.matchOffset - 1
- PreLzsDecoder.MaxMatch )
& ( PreLzsDecoder.DictionarySize - 1 );
this.position += this.matchLength + PreLzsDecoder.Threshold;
return offset;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// mark / reset
//------------------------------------------------------------------
// public void mark( int readLimit )
// public void reset()
// public boolean markSupported()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の 読み込み位置に
* 戻れるようにする。
* InputStream の mark() と違い、readLimit で設定した
* 限界バイト数より前にマーク位置が無効になる可能性が
* ある事に注意すること。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み
* 込んだ場合 reset()できなくなる可
* 能性がある。
*
* @see PreLzssDecoder#mark(int)
*/
public void mark( int readLimit ){
this.in.mark( ( readLimit * 9 + 7 ) / 8 + 1 );
this.markPosition = this.position;
this.markMatchOffset = this.matchOffset;
this.markMatchLength = this.matchLength;
}
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException
* (1) mark() せずに reset() しようとした場合。
* (2) 接続された入力ストリームが markSupported()で
* false を返す場合。
* (3) 接続された入力ストリームで
* 入出力エラーが発生した場合。
* の何れか。
*/
public void reset() throws IOException {
//mark()しないで reset()しようとした場合、
//接続された InputStream が mark/resetをサポートしない場合は
//BitInputStream の reset()によって IOExceptionが投げられる。
this.in.reset(); //throws IOException
this.position = this.markPosition;
this.matchOffset = this.markMatchOffset;
this.matchLength = this.markMatchLength;
}
/**
* 接続された入力ストリームが mark() と reset() を
* サポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public boolean markSupported(){
return this.in.markSupported();
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// other
//------------------------------------------------------------------
// public int available()
// public void close()
//------------------------------------------------------------------
/**
* ブロックせずに読み出すことの出来る最低バイト数を得る。
* InputStream の available() と違い、
* この最低バイト数は保証される。
*
* @return ブロックしないで読み出せる最低バイト数。
*
* @exception IOException 入出力エラーが発生した場合
*
* @see PreLzssDecoder#available()
*/
public int available() throws IOException {
return Math.max( this.in.availableBits() / 9 - 2, 0 );
}
/**
* この出力とストリームと
* 接続されていたストリームを閉じ、
* 使用していたリソースを解放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public void close() throws IOException {
this.in.close();
this.in = null;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.PreLzssDecoder
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public int getDictionarySize()
// public int getMaxMatch()
// public int getThreshold()
//------------------------------------------------------------------
/**
* -lzs-形式の LZSS辞書のサイズを得る。
*
* @return -lzs-形式の LZSS辞書のサイズ
*/
public int getDictionarySize(){
return PreLzsDecoder.DictionarySize;
}
/**
* -lzs-形式の LZSSの最長一致長を得る。
*
* @return -lzs-形式の LZSSの最長一致長
*/
public int getMaxMatch(){
return PreLzsDecoder.MaxMatch;
}
/**
* -lzs-形式の LZSSの圧縮、非圧縮の閾値を得る。
*
* @return -lzs-形式の LZSSの圧縮、非圧縮の閾値
*/
public int getThreshold(){
return PreLzsDecoder.Threshold;
}
}
//end of PreLzsDecoder.java
jp/gr/java_conf/dangan/util/lha/PreLzssDecoder.java 100644 0 0 16006 7517637600 17676 0 ustar 0 0 //start of PreLzssDecoder.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* PreLzssDecoder.java
*
* Copyright (C) 2001 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
//import exceptions
import java.io.IOException;
import java.io.EOFException;
/**
* LZSS圧縮コードを供給するインターフェイス。
*
*
* -- revision history --
* $Log: PreLzssDecoder.java,v $
* Revision 1.0 2002/07/25 00:00:00 dangan
* add to version control
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public interface PreLzssDecoder{
//------------------------------------------------------------------
// original method ( on the model of java.io.InputStream )
//------------------------------------------------------------------
// mark/reset
//------------------------------------------------------------------
// public abstract void mark( int readLimit )
// public abstract void reset()
// public abstract boolean markSupported()
//------------------------------------------------------------------
/**
* 接続された入力ストリームの現在位置にマークを設定し、
* reset() メソッドでマークした時点の読み込み位置に戻れるようにする。
* InputStream の mark() と違い、 readLimit で設定した
* 限界バイト数より前にマーク位置が無効になってもかまわない。
* ただし、readLimit を無視して無限に reset() 可能な
* InputStream と接続している場合は readLimit にどのような値を設定されても
* reset() で必ずマーク位置に復旧できなければならない。
*
* @param readLimit マーク位置に戻れる限界のバイト数。
* このバイト数を超えてデータを読み込んだ場合
* reset()できなくなる可能性がある。
*/
public abstract void mark( int readLimit );
/**
* 接続された入力ストリームの読み込み位置を最後に
* mark() メソッドが呼び出されたときの位置に設定する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract void reset() throws IOException;
/**
* 接続された入力ストリームが mark() と reset() を
* サポートするかを得る。
*
* @return ストリームが mark() と reset() を
* サポートする場合は true。
* サポートしない場合は false。
*/
public abstract boolean markSupported();
//------------------------------------------------------------------
// original method ( on the model of java.io.InputStream )
//------------------------------------------------------------------
// other method
//------------------------------------------------------------------
// public abstract int available()
// public abstract void close()
//------------------------------------------------------------------
/**
* 接続された入力ストリームからブロックしないで
* 読み込むことのできる最低バイト数を得る。
* この数値は完全である事を保障しなくてよい。
* これは故意に作成されたデータ等ではブロックせずに
* 読み込む事の出来る最低バイト数を得るには
* 実際に読み込んでみる以外に方法がないためである。
*
* @return ブロックしないで読み出せる最低バイト数。
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract int available() throws IOException;
/**
* この入力ストリームを閉じ、使用していた
* 全てのリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract void close() throws IOException;
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// read
//------------------------------------------------------------------
// public abstract int readCode()
// public abstract int readOffset()
//------------------------------------------------------------------
/**
* 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長を読み込む。
* 未圧縮データは 0〜255、
* LZSS圧縮コード(一致長)は 256〜511 の値を取らなければならない。
*
* @return 1byte の LZSS未圧縮のデータもしくは、
* LZSS で圧縮された圧縮コードのうち一致長
*
* @exception IOException 入出力エラーが発生した場合
* @exception EOFException EndOfStreamに達した場合
*/
public abstract int readCode() throws IOException;
/**
* LZSS で圧縮された圧縮コードのうち一致位置を読み込む。
*
* @return LZSS で圧縮された圧縮コードのうち一致位置
*
* @exception IOException 入出力エラーが発生した場合
*/
public abstract int readOffset() throws IOException;
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// get LZSS parameter
//------------------------------------------------------------------
// public abstract int getDictionarySize()
// public abstract int getMaxMatch()
// public abstract int getThreshold()
//------------------------------------------------------------------
/**
* このPreLzssDecoderが処理するLZSS辞書のサイズを得る。
*
* @return LZSS辞書のサイズ
*/
public abstract int getDictionarySize();
/**
* このPreLzssDecoderが処理する最長一致長を得る。
*
* @return 最長一致長
*/
public abstract int getMaxMatch();
/**
* このPreLzssDecoderが処理する圧縮、非圧縮の閾値を得る。
*
* @return LZSSの閾値
*/
public abstract int getThreshold();
}
//end of PreLzssDecoder.java
jp/gr/java_conf/dangan/util/lha/resources/lha.properties 100644 0 0 14176 7574505600 21042 0 ustar 0 0 ################################################################
# lha.properties
# $Revision: 1.1 $
#===============================================================
# setting file of LHA Library for Java
#===============================================================
# Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY MICHEL ISHIZUKA ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MICHEL
# ISHIZUKA BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
################################################################
#===============================================================
# key : lha.encoding
#---------------------------------------------------------------
# val : encoding name of Java.
# ( ex. ISO8859_1, ASCII etc. )
# this value is used for conversion of the character
# sequence in an LHA header.
# when encoding is not set up correctly, character may
# be garbled. it causes a file which may be unable to
# extract or cannot be deleted.
#===============================================================
lha.encoding=ISO8859_1
#lha.encoding=ASCII
#===============================================================
# key : lha.packages
#---------------------------------------------------------------
# val : enumeration of packages ( comma-deliminated )
# this value is used for creating a fully qualified class
# name from class name in generation formula.
#===============================================================
lha.packages=jp.gr.java_conf.dangan.util.lha
#===============================================================
# key : lha.lzs.encoder
# lha.lz5.encoder
# lha.lh1.encoder
# lha.lh2.encoder
# lha.lh3.encoder
# lha.lh4.encoder
# lha.lh5.encoder
# lha.lh6.encoder
# lha.lh7.encoder
# lha.lz4.encoder
# lha.lh0.encoder
# lha.lhd.encoder
#---------------------------------------------------------------
# val : generation fomula of encoder which is corresponding
# compress method.
# This value is used for creating a encoder which is
# corresponding compress method in LhaOutputStream,
# LhaRetainedOutputStream and LhaImmediateOutputStream.
#===============================================================
lha.lzs.encoder=LzssOutputStream(PostLzsEncoder(out),HashAndChainedListSearch,[HashShort])
lha.lz5.encoder=LzssOutputStream(PostLz5Encoder(out),HashAndChainedListSearch)
lha.lh1.encoder=LzssOutputStream(PostLh1Encoder(out),HashAndChainedListSearch)
lha.lh2.encoder=LzssOutputStream(PostLh2Encoder(out),HashAndChainedListSearch)
lha.lh3.encoder=LzssOutputStream(PostLh3Encoder(out),HashAndChainedListSearch)
lha.lh4.encoder=LzssOutputStream(PostLh5Encoder(out,-lh4-),HashAndChainedListSearch)
lha.lh5.encoder=LzssOutputStream(PostLh5Encoder(out,-lh5-),HashAndChainedListSearch)
lha.lh6.encoder=LzssOutputStream(PostLh5Encoder(out,-lh6-),HashAndChainedListSearch)
lha.lh7.encoder=LzssOutputStream(PostLh5Encoder(out,-lh7-),HashAndChainedListSearch)
lha.lz4.encoder=out
lha.lh0.encoder=out
lha.lhd.encoder=out
#===============================================================
# key : lha.lzs.decoder
# lha.lz5.decoder
# lha.lh1.decoder
# lha.lh2.decoder
# lha.lh3.decoder
# lha.lh4.decoder
# lha.lh5.decoder
# lha.lh6.decoder
# lha.lh7.decoder
# lha.lz4.decoder
# lha.lh0.decoder
# lha.lhd.decoder
#---------------------------------------------------------------
# val : generation fomula of decoder which is corresponding
# compress method.
# This value is used for creating a decoder which is
# corresponding compress method in LhaFile and
# LhaInputStream.
# LhaOutputStream and LhaRetainedOutputStream use this
# value when compression failed.
#===============================================================
lha.lzs.decoder=LzssInputStream(PreLzsDecoder(in),length)
lha.lz5.decoder=LzssInputStream(PreLz5Decoder(in),length)
lha.lh1.decoder=LzssInputStream(PreLh1Decoder(in),length)
lha.lh2.decoder=LzssInputStream(PreLh2Decoder(in),length)
lha.lh3.decoder=LzssInputStream(PreLh3Decoder(in),length)
lha.lh4.decoder=LzssInputStream(PreLh5Decoder(in,-lh4-),length)
lha.lh5.decoder=LzssInputStream(PreLh5Decoder(in,-lh5-),length)
lha.lh6.decoder=LzssInputStream(PreLh5Decoder(in,-lh6-),length)
lha.lh7.decoder=LzssInputStream(PreLh5Decoder(in,-lh7-),length)
lha.lz4.decoder=in
lha.lh0.decoder=in
lha.lhd.decoder=in
#===============================================================
# key : lha.header
#---------------------------------------------------------------
# val : generation fomula of subclass of LhaHeader
# This value is used for creating a instance of LhaHeader
# in LhaFile, LhaInputStream.
#===============================================================
lha.header=LhaHeader(data,encoding)
jp/gr/java_conf/dangan/util/lha/resources/lha_ja.properties 100644 0 0 13741 7574505600 21511 0 ustar 0 0 ################################################################
# lha_ja.properties
# $Revision: 1.1 $
#===============================================================
# LHA Library for Java の設定ファイル
#===============================================================
# Copyright (C) 2002 Michel Ishizuka All rights reserved.
#
# 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
# 変更の有無にかかわらず許可する。
#
# 1.ソースコードの再配布において著作権表示と この条件のリスト
# および下記の声明文を保持しなくてはならない。
#
# 2.バイナリ形式の再配布において著作権表示と この条件のリスト
# および下記の声明文を使用説明書もしくは その他の配布物内に
# 含む資料に記述しなければならない。
#
# このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
# 的を達成できるという保証、商品価値が有るという保証にとどまらず、
# いかなる明示的および暗示的な保証もしない。
# 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
# 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
# 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
# サービスの導入費等が考えられるが、決してそれだけに限定されない
# 損害)に対して、いかなる事態の原因となったとしても、契約上の責
# 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
# 正行為のためであったとしても、またはそのような損害の可能性が報
# 告されていたとしても一切の責任を負わないものとする。
#
################################################################
#===============================================================
# key : lha.encoding
#---------------------------------------------------------------
# val : Javaのエンコーディング名。
# ( 例 ISO8859_1, ASCII, SJIS, 等 )
# この値はLHAヘッダ内の文字列を String に変換するために
# 使用される。
# もし、エンコーディング名が正しく設定されない場合、文字
# 化けの原因となる。この文字化けは、解凍できないファイル
# や、削除できないファイルを作成してしまう原因となる。
# また 日本語を扱う場合は シフトJIS系列のエンコーディング
# を指定すること。EUC系列やJIS系列のエンコーディングを使
# 用した書庫は混乱の元になるので、配布や公開は禁止とする。
# シフトJIS系列のエンコーディングの扱いの複雑さを考慮して、
# Javaのエンコーディング名にはない、ShiftJISAuto も使用で
# きる。これは、Javaのシステムプロパティ file.encoding を
# 参照してそれがシフトJIS系列であればそれを使用し、違えば
# SJIS を使用する。
#===============================================================
lha.encoding=ShiftJISAuto
#lha.encoding=MS932
#lha.encoding=SJIS
#lha.encoding=ISO8859_1
#===============================================================
# key : lha.lzs.encoder
# lha.lz5.encoder
# lha.lh1.encoder
# lha.lh2.encoder
# lha.lh3.encoder
# lha.lh4.encoder
# lha.lh5.encoder
# lha.lh6.encoder
# lha.lh7.encoder
# lha.lz4.encoder
# lha.lh0.encoder
# lha.lhd.encoder
#---------------------------------------------------------------
# val : 対応する圧縮法の符号器の生成式
# この値は LhaOutputStream、LhaRetainedOutputStream、
# LhaImmediateOutputStream が圧縮処理を行う際に
# 圧縮法に対応した符号器を生成するために使用される。
#===============================================================
lha.lzs.encoder=LzssOutputStream(PostLzsEncoder(out),HashAndChainedListSearch,[HashShort])
lha.lz5.encoder=LzssOutputStream(PostLz5Encoder(out),HashAndChainedListSearch)
lha.lh1.encoder=LzssOutputStream(PostLh1Encoder(out),HashAndChainedListSearch)
lha.lh2.encoder=LzssOutputStream(PostLh2Encoder(out),HashAndChainedListSearch)
lha.lh3.encoder=LzssOutputStream(PostLh3Encoder(out),HashAndChainedListSearch)
lha.lh4.encoder=LzssOutputStream(PostLh5Encoder(out,-lh4-),HashAndChainedListSearch)
lha.lh5.encoder=LzssOutputStream(PostLh5Encoder(out,-lh5-),HashAndChainedListSearch)
lha.lh6.encoder=LzssOutputStream(PostLh5Encoder(out,-lh6-),HashAndChainedListSearch)
lha.lh7.encoder=LzssOutputStream(PostLh5Encoder(out,-lh7-),HashAndChainedListSearch)
lha.lz4.encoder=out
lha.lh0.encoder=out
lha.lhd.encoder=out
#===============================================================
# key : lha.lzs.decoder
# lha.lz5.decoder
# lha.lh1.decoder
# lha.lh2.decoder
# lha.lh3.decoder
# lha.lh4.decoder
# lha.lh5.decoder
# lha.lh6.decoder
# lha.lh7.decoder
# lha.lz4.decoder
# lha.lh0.decoder
# lha.lhd.decoder
#---------------------------------------------------------------
# val : 対応する圧縮法の復号器の生成式
# この値は LhaInputStream、LhaFile が解凍処理を行う際に
# 圧縮法に対応した復号器を生成するために使用される。
# また LhaOutputStream、LhaRetainedOutputStream で
# 圧縮に失敗した際の解凍のためにも使用される。
#===============================================================
lha.lzs.decoder=LzssInputStream(PreLzsDecoder(in),length)
lha.lz5.decoder=LzssInputStream(PreLz5Decoder(in),length)
lha.lh1.decoder=LzssInputStream(PreLh1Decoder(in),length)
lha.lh2.decoder=LzssInputStream(PreLh2Decoder(in),length)
lha.lh3.decoder=LzssInputStream(PreLh3Decoder(in),length)
lha.lh4.decoder=LzssInputStream(PreLh5Decoder(in,-lh4-),length)
lha.lh5.decoder=LzssInputStream(PreLh5Decoder(in,-lh5-),length)
lha.lh6.decoder=LzssInputStream(PreLh5Decoder(in,-lh6-),length)
lha.lh7.decoder=LzssInputStream(PreLh5Decoder(in,-lh7-),length)
lha.lz4.decoder=in
lha.lh0.decoder=in
lha.lhd.decoder=in
#===============================================================
# key : lha.header
#---------------------------------------------------------------
# val : LhaHeader, もしくは LhaHeader のサブクラスの生成式。
# この値は LhaInputStream、LhaFile が読み込んだ
# ヘッダデータから LhaHeader のインスタンスを生成するため
# に使用される。
#===============================================================
lha.header=LhaHeader(data,encoding)
jp/gr/java_conf/dangan/util/lha/SimpleSearch.java 100644 0 0 23401 7523340000 17340 0 ustar 0 0 //start of SimpleSearch.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* SimpleSearch.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import jp.gr.java_conf.dangan.util.lha.LzssSearchMethod;
import jp.gr.java_conf.dangan.util.lha.LzssOutputStream;
//import exceptions
/**
* 特別な検索機構を用いない
* LzssSearchMethod の最もシンプルな実装。
* 検索機構を用いないため、
* 他の検索機構を用いる実装と比べると遅いが、
* メモリ消費量も非常に少ない。
*
*
* -- revision history --
* $Log: SimpleSearch.java,v $
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [change]
* LzssSearchMethod のインタフェイス変更にあわせてインタフェイス変更。
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class SimpleSearch implements LzssSearchMethod{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private int DictionarySize
// private int MaxMatch
// private int Threshold
//------------------------------------------------------------------
/**
* LZSS辞書サイズ。
*/
private int DictionarySize;
/**
* LZSS圧縮に使用される値。
* 最大一致長を示す。
*/
private int MaxMatch;
/**
* LZSS圧縮に使用される閾値。
* 一致長が この値以上であれば、圧縮コードを出力する。
*/
private int Threshold;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// text buffer
//------------------------------------------------------------------
// private byte[] TextBuffer
// private int DictionaryLimit
//------------------------------------------------------------------
/**
* LZSS圧縮を施すためのバッファ。
* position を境に 前半は辞書領域、
* 後半は圧縮を施すためのデータの入ったバッファ。
* LzssSearchMethodの実装内では読み込みのみ許される。
*/
private byte[] TextBuffer;
/**
* 辞書の限界位置。
* TextBuffer前半の辞書領域にデータが無い場合に
* 辞書領域にある不定のデータ(Javaでは0)を使用
* して圧縮が行われるのを抑止する。
*/
private int DictionaryLimit;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private SimpleSearch()
// public SimpleSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
*使用不可
*/
private SimpleSearch(){ }
/**
* 特別な検索機構を用いないシンプルな
* LzssSearchMethod を構築する。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最大一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
*/
public SimpleSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer ){
this.DictionarySize = DictionarySize;
this.MaxMatch = MaxMatch;
this.Threshold = Threshold;
this.TextBuffer = TextBuffer;
this.DictionaryLimit = this.DictionarySize;
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.LzssSearchMethod
//------------------------------------------------------------------
// public void put( int position )
// public int searchAndPut( int position )
// public int search( int position, int lastPutPos, int maxMatch )
// public void slide()
// public int putRequires()
//------------------------------------------------------------------
/**
* SimpleSearch は検索機構を使用しないため
* このメソッドは何もしない。
*
* @param position TextBuffer内のデータパタンの開始位置
*/
public void put( int position ){ }
/**
* TextBuffer 内の辞書領域にあるデータパタンから
* position から始まるデータパタンと
* 最長の一致を持つものを検索する。
*
* @param position TextBuffer内のデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int searchAndPut( int position ){
int matchlen = this.Threshold - 1;
int matchpos = position;
int scanlimit = Math.max( this.DictionaryLimit,
position - this.DictionarySize );
int scanpos = position - 1;
byte[] buf = this.TextBuffer;
int max = position + this.MaxMatch;
int p = 0;
int s = 0;
int len = 0;
while( scanlimit < scanpos ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
if( this.MaxMatch == len ) break;
}
scanpos--;
}
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* TextBuffer 内の辞書領域にあるデータパタンから
* position から始まるデータパタンと
* 最長の一致を持つものを検索する。
*
* @param position TextBuffer内のデータパタンの開始位置。
* @param lastPutPos 最後に登録したデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int search( int position, int lastPutPos ){
int matchlen = this.Threshold - 1;
int matchpos = position;
int scanlimit = Math.max( this.DictionaryLimit,
position - this.DictionarySize );
int scanpos = position - 1;
byte[] buf = this.TextBuffer;
int max = Math.min( position + this.MaxMatch,
this.TextBuffer.length );
int p = 0;
int s = 0;
int len = 0;
while( scanlimit < scanpos ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
if( this.MaxMatch == len ) break;
}
scanpos--;
}
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* LzssOutputStream が slide() でTextBuffer内のデータを
* DictionarySize だけ移動させる際に検索機構内のデータを
* それらと矛盾無く移動させる処理を行う。
*/
public void slide(){
this.DictionaryLimit = Math.max( 0, this.DictionaryLimit - this.DictionarySize );
}
/**
* SimpleSearch は検索機構を使用しないため常に 0 を返す。
*
* @return 常に 0
*/
public int putRequires(){
return 0;
}
}
//end of SimpleSearch.java
jp/gr/java_conf/dangan/util/lha/StaticHuffman.java 100644 0 0 62530 7517367000 17537 0 ustar 0 0 //start of StaticHuffman.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* StaticHuffman.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
//import exceptions
import jp.gr.java_conf.dangan.util.lha.BadHuffmanTableException;
/**
* 静的ハフマン用ユーティリティ関数群を保持する。
* ハフマン符号は最大16ビットに制限される。
*
*
* -- revision history --
* $Log: StaticHuffman.java,v $
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [maintenance]
* ソース整備
* タブ廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.0 $
*/
public class StaticHuffman{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// public static final int LimitLen
//------------------------------------------------------------------
/**
* LHAがDOSの16bitモードを使用して作られたことによる
* ハフマン符号長の制限。
*/
public static final int LimitLen = 16;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private StaticHuffman()
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private StaticHuffman(){ }
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// convert
//------------------------------------------------------------------
// public static int[] FreqListToLenList( int[] FreqList )
// public static int[] FreqListToLenListOriginal( int[] FreqList )
// public static int[] LenListToCodeList( int[] LenList )
//------------------------------------------------------------------
/**
* 頻度表から ハフマン符号のビット長の表を作成する。
*
* @param FreqList 頻度表
*
* @return ハフマン符号のビット長の表
*/
public static int[] FreqListToLenList( int[] FreqList ){
/**
* ハフマン木を構成する配列群
* ハフマン木は 0〜FreqList.length までは全てが葉であり、
* そのノード番号は符号である。木がいったん完成した後は
* TreeCount-1がルートノードとなる。
* NodeWeight:: そのノードの重みを持つ
* SmallNode:: 小さな子ノードのノード番号を持つ
* LargeNode:: 大きな子ノードのノード番号を持つ
* TreeCount:: 有効なノードの個数を持つ
*/
int[] NodeWeight = new int[ FreqList.length * 2 - 1 ];
int[] SmallNode = new int[ FreqList.length * 2 - 1 ];
int[] LargeNode = new int[ FreqList.length * 2 - 1 ];
int TreeCount = FreqList.length;
/**
* ハフマン木の葉のノード番号を小さな順に格納したリスト。
* Leafs:: リスト本体
* LeafCount:: 葉の個数
*/
int[] Leafs = new int[ FreqList.length ];
int LeafCount = 0;
/**
* ハフマン木の葉でないノードのノード番号を
* 小さな順に格納したリストを格納する。
* Nodes:: リスト本体
* NodeCount:: 葉でないノードの個数
*/
int[] Nodes = new int[ FreqList.length - 1 ];
int NodeCount = 0;
//木に葉をセットし、
//Leafsに頻度1以上の葉のみセットする。
for( int i = 0 ; i < FreqList.length ; i++ ){
NodeWeight[i] = FreqList[i];
if( 0 < FreqList[i] )
Leafs[ LeafCount++ ] = i;
}
if( 2 <= LeafCount ){
//=================================
//ハフマン木を作成する
//=================================
//ハフマン木の葉となるべき要素を整列させる。
StaticHuffman.MergeSort( Leafs, 0, LeafCount - 1,
FreqList, new int[ ( LeafCount / 2 ) + 1 ] );
//葉か、ノードの最小のもの2つを新しいノードに
//結びつける事を繰り返し、ルートノードまで作成する。
//この処理によってハフマン木が完成する。
int LeafIndex = 0;
int NodeIndex = 0;
do{
int small;
if( NodeCount <= NodeIndex )
small = Leafs[ LeafIndex++ ];
else if( LeafCount <= LeafIndex )
small = Nodes[ NodeIndex++ ];
else if( NodeWeight[Leafs[LeafIndex]] <= NodeWeight[Nodes[NodeIndex]] )
small = Leafs[ LeafIndex++ ];
else
small = Nodes[ NodeIndex++ ];
int large;
if( NodeCount <= NodeIndex )
large = Leafs[ LeafIndex++ ];
else if( LeafCount <= LeafIndex )
large = Nodes[ NodeIndex++ ];
else if( NodeWeight[Leafs[LeafIndex]] <= NodeWeight[Nodes[NodeIndex]] )
large = Leafs[ LeafIndex++ ];
else
large = Nodes[ NodeIndex++ ];
int newNode = TreeCount++;
NodeWeight[newNode] = NodeWeight[small] + NodeWeight[large];
SmallNode[newNode] = small;
LargeNode[newNode] = large;
Nodes[NodeCount++] = newNode;
}while( NodeIndex + LeafIndex < NodeCount + LeafCount - 1 );
//============================================
//ハフマン木からハフマン符号長の表を作成する。
//============================================
//ハフマン木からハフマン符号長の頻度表を作成する。
int[] LenFreq = StaticHuffman.HuffmanTreeToLenFreq( SmallNode,
LargeNode, TreeCount - 1 );
//ハフマン符号長の頻度長から符号長の表を作成する。
int[] LenList = new int[ FreqList.length ];
LeafIndex = 0;
for( int len = StaticHuffman.LimitLen ; 0 < len ; len-- )
while( 0 < LenFreq[len]-- )
LenList[Leafs[LeafIndex++]] = len;
return LenList;
}else{
return new int[ FreqList.length ];
}
}
/**
* 頻度表から ハフマン符号のビット長の表を作成する。
* オリジナルのLHAと同じコードを出力する。
*
* @param FreqList 頻度表
*
* @return ハフマン符号のビット長の表
*/
public static int[] FreqListToLenListOriginal( int[] FreqList ){
/**
* ハフマン木を構成する配列群
* ハフマン木は 0〜FreqList.length までは全てが葉であり、
* そのノード番号は符号である。木がいったん完成した後は
* TreeCount-1がルートノードとなる。
* NodeWeight:: そのノードの重みを持つ
* SmallNode:: 小さな子ノードのノード番号を持つ
* LargeNode:: 大きな子ノードのノード番号を持つ
* TreeCount:: 有効なノードの個数を持つ
*/
int[] NodeWeight = new int[ FreqList.length * 2 - 1 ];
int[] SmallNode = new int[ FreqList.length * 2 - 1 ];
int[] LargeNode = new int[ FreqList.length * 2 - 1 ];
int TreeCount = FreqList.length;
/**
* ハフマン木の葉のノード番号を小さな順に格納したリスト。
* Leafs:: リスト本体
* LeafCount:: 葉の個数
*/
int[] Leafs = new int[ FreqList.length ];
int LeafCount = 0;
/**
* ハフマン木の全てのノードのノード番号を
* 小さな順に格納したリストを格納する。
* ヒープソートを使用するため、Heap[0]は使用しない
* Heap:: リスト本体
* HeapLast:: Heapの最後の要素
*/
int[] Heap = new int[ FreqList.length * 2 ];
int HeapLast = 0;
//木に葉をセットし、
//Heapに頻度1以上の葉のみセットする。
for( int i = 0 ; i < FreqList.length ; i++ ){
NodeWeight[i] = FreqList[i];
if( 0 < FreqList[i] )
Heap[ ++HeapLast ] = i;
}
if( 2 <= HeapLast ){
//=================================
//ハフマン木を作成する
//=================================
//ハフマン木の葉となるべき要素を整列させる。
for( int i = HeapLast / 2 ; 1 <= i ; i-- )
StaticHuffman.DownHeap( Heap, HeapLast, NodeWeight, i );
//葉か、ノードの最小のもの2つを新しいノードに
//結びつける事を繰り返し、ルートノードまで作成する。
//この処理によってハフマン木が完成する。
do{
int small = Heap[1];
if( small < FreqList.length ) Leafs[LeafCount++] = small;
Heap[1] = Heap[HeapLast--];
StaticHuffman.DownHeap( Heap, HeapLast, NodeWeight, 1 );
int large = Heap[1];
if( large < FreqList.length ) Leafs[LeafCount++] = large;
int newNode = TreeCount++;
NodeWeight[newNode] = NodeWeight[small] + NodeWeight[large];
SmallNode[newNode] = small;
LargeNode[newNode] = large;
Heap[1] = newNode;
StaticHuffman.DownHeap( Heap, HeapLast, NodeWeight, 1 );
}while( 1 < HeapLast );
//============================================
//ハフマン木からハフマン符号長の表を作成する。
//============================================
//ハフマン木からハフマン符号長の頻度表を作成する。
int[] LenFreq = StaticHuffman.HuffmanTreeToLenFreq( SmallNode,
LargeNode, TreeCount - 1 );
//ハフマン符号長の頻度長から符号長の表を作成する。
int[] LenList = new int[ FreqList.length ];
int LeafIndex = 0;
for( int len = StaticHuffman.LimitLen ; 0 < len ; len-- )
while( 0 < LenFreq[len]-- )
LenList[Leafs[LeafIndex++]] = len;
return LenList;
}else{
return new int[ FreqList.length ];
}
}
/**
* ハフマン符号長のリストから ハフマン符号表を作成する。
*
* @param LenList ハフマン符号長のリスト
*
* @return ハフマン符号表
*
* @exception BadHuffmanTableException
* LenListが不正なため、
* ハフマン符号表が生成出来ない場合
*/
public static int[] LenListToCodeList( int[] LenList )
throws BadHuffmanTableException {
//ハフマン符号長の頻度表
int[] LenFreq = new int[ StaticHuffman.LimitLen + 1 ];
//ハフマン符号長に対応した符号
int[] CodeStart = new int[ StaticHuffman.LimitLen + 2 ];
//ハフマン符号長の頻度表作成
for( int i = 0 ; i < LenList.length ; i++ )
LenFreq[LenList[i]]++;
if( LenFreq[0] < LenList.length ){
//CodeStart[1] = 0; //Javaでは必要無いのでコメントアウトしている。
for( int i = 1 ; i <= StaticHuffman.LimitLen ; i++ )
CodeStart[i + 1] = CodeStart[i] + LenFreq[i] << 1;
if( CodeStart[ StaticHuffman.LimitLen + 1 ] != 0x20000 )
throw new BadHuffmanTableException();
int[] CodeList = new int[ LenList.length ];
for( int i = 0 ; i < CodeList.length ; i++ )
if( 0 < LenList[i] )
CodeList[i] = CodeStart[ LenList[i] ]++;
return CodeList;
}else{
return new int[ LenList.length ];
}
}
//------------------------------------------------------------------
// shared method
//------------------------------------------------------------------
// utility for decoder
//------------------------------------------------------------------
// public static short[] createTable( int[] LenList )
// public static short[][] createTableAndTree( int[] LenList, int TableBits )
//------------------------------------------------------------------
/**
* LenList から、ハフマン復号用のテーブルを生成する。
*
* @param LenList ハフマン符号長の表
*
* @return ハフマン復号用テーブル。
*
* @exception BadHuffmanTableException
* LenListが不正なため、
* ハフマン符号表が生成出来ない場合
*/
public static short[] createTable( int[] LenList )
throws BadHuffmanTableException {
int[] CodeList = StaticHuffman.LenListToCodeList( LenList ); //throws BadHuffmanTableException
int TableBits = 0;
int LastCode = 0;
for( int i = 0 ; i < LenList.length ; i++ ){
if( TableBits <= LenList[i] ){
TableBits = LenList[i];
LastCode = i;
}
}
short[] Table = new short[ 1 << TableBits ];
for( int i = 0 ; i < LenList.length ; i++ ){
if( 0 < LenList[i] ){
int start = CodeList[i] << ( TableBits - LenList[i] );
int end = ( i != LastCode
? start + ( 1 << ( TableBits - LenList[i] ) )
: Table.length );
for( int j = start ; j < end ; j++ )
Table[j] = (short)i;
}
}
return Table;
}
/**
* LenList から、ハフマン復号用のテーブルと木を生成する。
* テーブルは TableBits の大きさを持ち、それ以上の部分は木に格納される。
* 戻り値は new short[][]{ Table, Tree[0], Tree[1] } となる。
* テーブルを引いた結果もしくは木を走査した際、負の値を得た場合、
* それは復号化されたコードを全ビット反転したものである。
* 正の値であればそれは 木を走査するための index であり、
* Tree[bit][index] のように使用する。
*
* @param LenList ハフマン符号長の表
* @param TableBits ハフマン復号用テーブルの大きさ。
*
* @return ハフマン復号用テーブルと木。
*
* @exception BadHuffmanTableException
* LenListが不正なため、
* ハフマン符号表が生成出来ない場合
*/
public static short[][] createTableAndTree( int[] LenList, int TableBits )
throws BadHuffmanTableException {
//------------------------------------------------------------------
//ハフマン符号長リストから ハフマン符号のリストを得る。
int[] CodeList = StaticHuffman.LenListToCodeList( LenList ); //throws BadHuffmanTableException
//------------------------------------------------------------------
//ハフマン符号長のリストを走査し、
//LastCode を得る。
//また 木を構成するのに必要な配列サイズを得るための準備を行う。
short[] Table = new short[ 1 << TableBits ];
int LastCode = 0;
for( int i = 0 ; i < LenList.length ; i++ ){
if( LenList[LastCode] <= LenList[i] ) LastCode = i;
if( TableBits < LenList[i] ){
Table[ CodeList[i] >> ( LenList[i] - TableBits ) ]++;
}
}
//------------------------------------------------------------------
//木を構成するのに必要な配列サイズを得、テーブルを初期化する。
final short INIT = -1;
int count = 0;
for( int i = 0 ; i < Table.length ; i++ ){
if( 0 < Table[i] ) count += Table[i] - 1;
Table[i] = INIT;
}
short[] Small = new short[ count ];
short[] Large = new short[ count ];
//------------------------------------------------------------------
//テーブルと木を構成する。
int avail = 0;
for( int i = 0 ; i < LenList.length ; i++ ){
if( 0 < LenList[i] ){
int TreeBits = LenList[i] - TableBits;
if( TreeBits <= 0 ){
int start = CodeList[i] << ( TableBits - LenList[i] );
int end = ( i != LastCode
? start + ( 1 << ( TableBits - LenList[i] ) )
: Table.length );
for( int j = start ; j < end ; j++ ){
Table[ j ] = (short)~i;
}
}else{
int TableCode = CodeList[i] >> TreeBits;
int node;
if( Table[ TableCode ] == INIT ){
node = Table[ TableCode ] = (short)(avail++);
}else{
node = Table[ TableCode ];
}
for( int j = TableBits + 1 ; j < LenList[i] ; j++ ){
if( 0 == ( CodeList[i] & ( 1 << ( LenList[i] - j ) ) ) ){
if( Small[node] == 0 ) node = Small[node] = (short)(avail++);
else node = Small[node];
}else{
if( Large[node] == 0 ) node = Large[node] = (short)(avail++);
else node = Large[node];
}
}
if( 0 == ( CodeList[i] & 0x01 ) ) Small[node] = (short)~i;
else Large[node] = (short)~i;
}
}
}
return new short[][]{ Table, Small, Large };
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// stuff of converter
//------------------------------------------------------------------
// private static void MergeSort( int[] array, int first, int last,
// int[] weight, int[] work )
// private static int[] HuffmanTreeToLenFreq( int[] SmallNode,
// int[] LargeNode, int root )
// private static void internalCountLenFreq( int[] SmallNode,
// int[] LargeNode, int node, int len, int[] LenFreq )
//------------------------------------------------------------------
/**
* マージソート、再帰関数
* arrayは weightの添字、arrayのfirstからlastの区間内で
* weightが小さい順に並ぶようにソートする。
* workはそのための作業領域。
*
* @param array ソート対象の配列
* @param first ソート区間の最初
* @param last ソート区間の最後
* @param weight ソートの際に参照される重みのリスト
* @param work マージソート用作業領域
*/
private static void MergeSort( int[] array,
int first,
int last,
int[] weight,
int[] work ){
if( first < last ){
int middle = ( first + last ) / 2 + ( first + last ) % 2;
//前半をソート
StaticHuffman.MergeSort( array, first, middle - 1, weight, work );
//後半をソート
StaticHuffman.MergeSort( array, middle, last, weight, work );
//前半を workへ
System.arraycopy( array, first, work, 0, middle - first );
//ソートされた前半と ソートされた後半を
//整列しつつマージする。
int srcIndex = middle;
int workIndex = 0;
int dstIndex = first;
while( srcIndex <= last && workIndex < middle - first )
array[ dstIndex++ ] =
( weight[work[workIndex]] < weight[array[srcIndex]]
? work[ workIndex++ ] : array[ srcIndex++ ] );
//workに残った要素を arrayに戻す
if( workIndex < middle - first )
System.arraycopy( work, workIndex, array, dstIndex,
middle - first - workIndex );
}
}
/**
* heapはweightの添え字
* num*2, num*2+1の地点でヒープが出来ていることを
* 前提として heap に numを頂点とするヒープを作る。
* ヒープソートの一部分。
*
* @param heap ヒープを生成する配列
* @param size ヒープのサイズ
* @param weight 整列の基準となる重みのリスト
* @param num 今回作成するヒープの頂点
*/
private static void DownHeap( int[] heap, int size, int[] weight, int num ){
int top = heap[num];
int i;
while( ( i = 2 * num ) <= size ){
if( i < size && weight[heap[i]] > weight[heap[i + 1]] ) i++;
if( weight[top] <= weight[heap[i]] ) break;
heap[num] = heap[i];
num = i;
}
heap[num] = top;
}
/**
* ハフマン木から ハフマン符号長の頻度表を作成する。
* ハフマン木を辿って ハフマン符号長の頻度表を作成する。
* また、符号長を 16ビットに制限するための処理もここで行う。
*
* @param SmallNode 小さい子ノードのノード番号の表
* @param LargeNode 大きい子ノードのノード番号の表
* @param root ハフマン木のルートノード
*
* @return ハフマン符号長を最大16ビットに制限した
* ハフマン符号長表
*/
private static int[] HuffmanTreeToLenFreq( int[] SmallNode,
int[] LargeNode,
int root ){
int[] LenFreq = new int[ StaticHuffman.LimitLen + 1 ];
//ハフマン木から頻度表作成
StaticHuffman.internalHuffmanTreeToLenFreq( SmallNode, LargeNode,
root, 0, LenFreq );
// System.out.println( "到達::StaticHuffman.HuffmanTreeToLenFreq--ハフマン木からハフマン符号長のリスト取得--" );
//最大16ビットの制限により、修正を受けている場合は
//符号長の表から、上位のノードを下位へと引きずりおろす
//ことによって符号長の表を修正する。
int weight = 0;
for( int i = StaticHuffman.LimitLen ; 0 < i ; i-- )
weight += LenFreq[i] << ( StaticHuffman.LimitLen - i );
// System.out.println( "weight::" + weight );
while( ( 1 << StaticHuffman.LimitLen ) < weight ){
LenFreq[ StaticHuffman.LimitLen ]--;
for( int i = StaticHuffman.LimitLen - 1 ; 0 < i ; i-- )
if( 0 < LenFreq[i] ){
LenFreq[i]--;
LenFreq[i + 1] += 2;
break;
}
weight--;
}
return LenFreq;
}
/**
* ハフマン木探索メソッド、再帰関数。
* ハフマン木を探索していき、nodeが葉であれば
* 渡された符号長の頻度表を更新し、
* ノードであれば、小さい方と大きい方の両方の
* 子ノードを再帰的に探索する。
*
* @param SmallNode 小さい子ノードのノード番号の表
* @param LargeNode 大きい子ノードのノード番号の表
* @param node 処理するノード番号
* @param len ハフマン木のrootからの長さ
* @param LenFreq 符号長の頻度表
*/
private static void internalHuffmanTreeToLenFreq( int[] SmallNode,
int[] LargeNode,
int node,
int len,
int[] LenFreq ){
if( node < ( SmallNode.length + 1 ) / 2 ){
//nodeが葉なら頻度表更新
LenFreq[ ( len < StaticHuffman.LimitLen
? len : StaticHuffman.LimitLen ) ]++;
}else{
//nodeがノードなら両方のノードを再帰的に探索
StaticHuffman.internalHuffmanTreeToLenFreq( SmallNode, LargeNode,
SmallNode[node], len + 1, LenFreq );
StaticHuffman.internalHuffmanTreeToLenFreq( SmallNode, LargeNode,
LargeNode[node], len + 1, LenFreq );
}
}
}
//end of StaticHuffman.java
jp/gr/java_conf/dangan/util/lha/TwoLevelHashSearch.java 100644 0 0 72013 7575462560 20506 0 ustar 0 0 //start of TwoLevelHashSearch.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* TwoLevelHashSearch.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util.lha;
//import classes and interfaces
import jp.gr.java_conf.dangan.io.Bits;
import jp.gr.java_conf.dangan.lang.reflect.Factory;
import jp.gr.java_conf.dangan.util.lha.HashShort;
import jp.gr.java_conf.dangan.util.lha.HashMethod;
import jp.gr.java_conf.dangan.util.lha.LzssOutputStream;
import jp.gr.java_conf.dangan.util.lha.LzssSearchMethod;
//import exceptions
import java.io.IOException;
import java.lang.NoSuchMethodException;
import java.lang.ClassNotFoundException;
import java.lang.InstantiationException;
import java.lang.reflect.InvocationTargetException;
import java.lang.Error;
import java.lang.NoSuchMethodError;
import java.lang.InstantiationError;
import java.lang.NoClassDefFoundError;
/**
* 二段階ハッシュと単方向連結リストを使って高速化された LzssSearchMethod。
* 定兼氏の論文
* を参考にした。
*
*
* -- revision history --
* $Log: TwoLevelHashSearch.java,v $
* Revision 1.1 2002/12/10 22:06:40 dangan
* [bug fix]
* searchAndPut() で最近の最長一致を取れなかったバグを修正。
*
* Revision 1.0 2002/12/03 00:00:00 dangan
* first edition
* add to version control
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class TwoLevelHashSearch implements LzssSearchMethod{
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// LZSS parameter
//------------------------------------------------------------------
// private int DictionarySize
// private int MaxMatch
// private int Threshold
//------------------------------------------------------------------
/**
* LZSS辞書サイズ。
*/
private int DictionarySize;
/**
* LZSS圧縮に使用される値。
* 最大一致長を示す。
*/
private int MaxMatch;
/**
* LZSS圧縮に使用される閾値。
* 一致長が この値以上であれば、圧縮コードを出力する。
*/
private int Threshold;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// text buffer
//------------------------------------------------------------------
// private byte[] TextBuffer
// private int DictionaryLimit
//------------------------------------------------------------------
/**
* LZSS圧縮を施すためのバッファ。
* 前半は辞書領域、
* 後半は圧縮を施すためのデータの入ったバッファ。
* LzssSearchMethodの実装内では読み込みのみ許される。
*/
private byte[] TextBuffer;
/**
* 辞書の限界位置。
* TextBuffer前半の辞書領域にデータが無い場合に
* 辞書領域にある不定のデータ(Javaでは0)を使用
* して圧縮が行われるのを抑止する。
*/
private int DictionaryLimit;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// primary hash
//------------------------------------------------------------------
// private HashMethod primaryHash
// private int[] primaryHashTable
// private int[] primaryCount
//------------------------------------------------------------------
/**
* 一段目のハッシュ関数
*/
private HashMethod primaryHash;
/**
* 一段目のハッシュテーブル
* 添字は一段目のハッシュ値、内容は 二段目のハッシュテーブルの index
*/
private int[] primaryHashTable;
/**
* 一段目のハッシュテーブルに幾つのデータパタンが
* 登録されているかをカウントしておく。
*/
private int[] primaryCount;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// secondary hash
//------------------------------------------------------------------
// private int[] secondaryHashRequires
// private int[] secondaryHashTable
// private int[] dummy
//------------------------------------------------------------------
/**
* 二段目のハッシュ値を算出するために必要なバイト数。
*/
private int[] secondaryHashRequires;
/**
* 二段目のハッシュテーブル
* 添字は 一段目のハッシュテーブルの値 + 二段目のハッシュ値、
* 内容は TextBuffer 内のデータパタンの開始位置
*/
private int[] secondaryHashTable;
/**
* slide() の毎に secondaryHashTable と入れ替えるダミー配列。
* 使いまわし用。
*/
private int[] dummy;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// cahined list
//------------------------------------------------------------------
// private int[] prev
//------------------------------------------------------------------
/**
* 同じハッシュ値を持つデータパタン開始位置を持つ
* 単方向連結リスト。
*/
private int[] prev;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// private TwoLevelHashSearch()
// public TwoLevelHashSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer )
// public TwoLevelHashSearch( int DictionarySize, int MaxMatch,
// int Threshold, byte[] TextBuffer,
// String HashMethodClassName )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 使用不可。
*/
private TwoLevelHashSearch(){ }
/**
* 二段階ハッシュを使用した LzssSearchMethod を構築する。
* 一段目のハッシュ関数には デフォルトのものが使用される。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最大一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
*/
public TwoLevelHashSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer ){
this( DictionarySize,
MaxMatch,
Threshold,
TextBuffer,
HashShort.class.getName() );
}
/**
* 二段階ハッシュを使用した LzssSearchMethod を構築する。
*
* @param DictionarySize 辞書サイズ
* @param MaxMatch 最大一致長
* @param Threshold 圧縮、非圧縮の閾値
* @param TextBuffer LZSS圧縮を施すためのバッファ
* @param HashMethodClassName Hash関数を提供するクラス名
*
* @exception NoClassDefFoundError
* HashMethodClassName で与えられたクラスが見つからない場合。
* @exception InstantiationError
* HashMethodClassName で与えられたクラスが
* abstract class であるためインスタンスを生成できない場合。
* @exception NoSuchMethodError
* HashMethodClassName で与えられたクラスが
* コンストラクタ HashMethod( byte[] )を持たない場合。
*/
public TwoLevelHashSearch( int DictionarySize,
int MaxMatch,
int Threshold,
byte[] TextBuffer,
String HashMethodClassName ){
this.DictionarySize = DictionarySize;
this.MaxMatch = MaxMatch;
this.Threshold = Threshold;
this.TextBuffer = TextBuffer;
this.DictionaryLimit = this.DictionarySize;
try{
this.primaryHash = (HashMethod)Factory.createInstance(
HashMethodClassName,
new Object[]{ TextBuffer } );
}catch( ClassNotFoundException exception ){
throw new NoClassDefFoundError( exception.getMessage() );
}catch( InvocationTargetException exception ){
throw new Error( exception.getTargetException().getMessage() );
}catch( NoSuchMethodException exception ){
throw new NoSuchMethodError( exception.getMessage() );
}catch( InstantiationException exception ){
throw new InstantiationError( exception.getMessage() );
}
// ハッシュテーブル初期化
this.primaryHashTable = new int[ this.primaryHash.tableSize() ];
this.secondaryHashTable = new int[ ( this.primaryHash.tableSize()
+ this.DictionarySize / 4 ) ];
for( int i = 0 ; i < this.primaryHashTable.length ; i++ ){
this.primaryHashTable[i] = i;
this.secondaryHashTable[i] = -1;
}
// その他の配列生成
// primaryCount と secondaryHashRequires は配列生成時にゼロクリアされている事を利用する。
this.primaryCount = new int[ this.primaryHash.tableSize() ];
this.secondaryHashRequires = new int[ this.primaryHash.tableSize() ];
this.dummy = new int[ this.secondaryHashTable.length ];
// 連結リスト初期化
this.prev = new int[ this.DictionarySize ];
for( int i = 0 ; i < this.prev.length ; i++ ){
this.prev[i] = -1;
}
}
//------------------------------------------------------------------
// method of jp.gr.java_conf.dangan.util.lha.LzssSearchMethod
//------------------------------------------------------------------
// public void put( int position )
// public int searchAndPut( int position )
// public int search( int position, int lastPutPos )
// public void slide( int slideWidth, int slideEnd )
// public int putRequires()
//------------------------------------------------------------------
/**
* position から始まるデータパタンを
* 二段階ハッシュと連結リストから成る検索機構に登録する。
*
* @param position TextBuffer内のデータパタンの開始位置
*/
public void put( int position ){
int phash = this.primaryHash.hash( position );
int base = this.primaryHashTable[ phash ];
int shash = this.secondaryHash( position, this.secondaryHashRequires[ phash ] );
this.primaryCount[ phash ]++;
this.prev[ position & ( this.DictionarySize - 1 ) ] =
this.secondaryHashTable[ base + shash ];
this.secondaryHashTable[ base + shash ] = position;
}
/**
* 二段階ハッシュと連結リストから成る検索機構に登録された
* データパタンから position から始まるデータパタンと
* 最長の一致を持つものを検索し、
* 同時に position から始まるデータパタンを
* 二段階ハッシュと連結リストから成る検索機構に登録する。
*
* @param position TextBuffer内のデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int searchAndPut( int position ){
int matchlen = this.Threshold - 1;
int matchpos = position;
int scanlimit = Math.max( this.DictionaryLimit,
position - this.DictionarySize );
int phash = this.primaryHash.hash( position );
int base = this.primaryHashTable[ phash ];
int requires = this.secondaryHashRequires[ phash ];
int shash = this.secondaryHash( position, requires );
int scanpos = this.secondaryHashTable[ base + shash ];
byte[] buf = this.TextBuffer;
int max = position + this.MaxMatch;
int s = 0;
int p = 0;
int len = 0;
//------------------------------------------------------------------
// 二段目のハッシュによって選ばれた連結リストを検索するループ
while( scanlimit <= scanpos ){
if( buf[ scanpos + matchlen ] == buf[ position + matchlen ] ){
s = scanpos;
p = position;
while( buf[s] == buf[p] ){
s++;
p++;
if( max <= p ) break;
}
len = p - position;
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
if( max <= p ) break;
}
}
scanpos = this.prev[ scanpos & ( this.DictionarySize - 1 ) ];
}
//------------------------------------------------------------------
// 二段目のハッシュによって厳選された連結リストに一致が無い場合、
// 一段目のハッシュに登録されている全ての連結リストを検索する
int revbits = 1;
int loopend = requires - Math.max( 0, this.Threshold - this.primaryHash.hashRequires() );
int maxmatch = this.primaryHash.hashRequires() + requires - 1;
for( int i = 1, send = 4 ; i <= loopend && matchlen <= maxmatch ; i++, send <<= 2 ){
max += position + maxmatch;
while( revbits < send ){
scanpos = this.secondaryHashTable[ base + ( shash ^ revbits ) ];
while( scanlimit <= scanpos ){
if( buf[ scanpos ] == buf[ position ] ){
s = scanpos + 1;
p = position + 1;
while( buf[s] == buf[p] ){
s++;
p++;
if( max <= p ) break;
}
len = p - position;
if( matchlen < len
|| ( matchlen == len && matchpos < scanpos ) ){
matchpos = scanpos;
matchlen = len;
if( max <= p ){
scanlimit = scanpos;
break;
}
}
}
scanpos = this.prev[ scanpos & ( this.DictionarySize - 1 ) ];
}
revbits++;
}
maxmatch = this.primaryHash.hashRequires() + requires - i - 1;
}
//------------------------------------------------------------------
// 二段階ハッシュと連結リストを使用した検索機構に
// position から始まるデータパタンを登録する。
this.primaryCount[ phash ]++;
this.prev[ position & ( this.DictionarySize - 1 ) ] =
this.secondaryHashTable[ base + shash ];
this.secondaryHashTable[ base + shash ] = position;
//------------------------------------------------------------------
// 最長一致を呼び出し元に返す。
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* ハッシュと連結リストを使用した検索機構に登録された
* データパタンを検索し position から始まるデータパタンと
* 最長の一致を持つものを得る。
*
* @param position TextBuffer内のデータパタンの開始位置。
* @param lastPutPos 最後に登録したデータパタンの開始位置。
*
* @return 一致が見つかった場合は
* LzssOutputStream.createSearchReturn
* によって生成された一致位置と一致長の情報を持つ値、
* 一致が見つからなかった場合は
* LzssOutputStream.NOMATCH。
*
* @see LzssOutputStream#createSearchReturn(int,int)
* @see LzssOutputStream#NOMATCH
*/
public int search( int position, int lastPutPos ){
//------------------------------------------------------------------
// ハッシュと連結リストによる検索機構に登録されていない
// データパタンを単純な逐次検索で検索する。
int matchlen = this.Threshold - 1;
int matchpos = position;
int scanpos = position - 1;
int scanlimit = Math.max( this.DictionaryLimit, lastPutPos );
byte[] buf = this.TextBuffer;
int max = Math.min( this.TextBuffer.length,
position + this.MaxMatch );
int s = 0;
int p = 0;
int len = 0;
while( scanlimit < scanpos ){
s = scanpos;
p = position;
while( buf[ s ] == buf[ p ] ){
s++;
p++;
if( max <= p ) break;
}
if( matchlen < len ){
matchpos = scanpos;
matchlen = len;
}
scanpos--;
}
//------------------------------------------------------------------
// 二段階ハッシュと連結リストを使用した検索機構から検索する。
int phashRequires = this.primaryHash.hashRequires();
if( phashRequires < this.TextBuffer.length - position ){
int phash = this.primaryHash.hash( position );
int base = this.primaryHashTable[ phash ];
int requires = this.secondaryHashRequires[ phash ];
int shash;
int start;
if( phashRequires + requires < this.TextBuffer.length - position ){
shash = this.secondaryHash( position, requires );
start = 0;
}else{
int avail = this.TextBuffer.length - position - phashRequires;
shash = this.secondaryHash( position, avail ) << ( ( requires - avail ) * 2 );
start = requires - avail;
}
int revbits = 0;
int loopend = requires - Math.max( 0, this.Threshold - this.primaryHash.hashRequires() );
int maxmatch = this.MaxMatch;
//------------------------------------------------------------------
// 一段目のに登録されている連結リストを優先度の順に検索するループ
for( int i = start, send = ( 1 << ( i * 2 ) ) ; i <= requires ; i++, send <<= 2 ){
max += position + maxmatch;
while( revbits < send ){
scanpos = this.secondaryHashTable[ base + ( shash ^ revbits ) ];
while( scanlimit <= scanpos ){
if( buf[ scanpos ] == buf[ position ] ){
s = scanpos + 1;
p = position + 1;
while( buf[s] == buf[p] ){
s++;
p++;
if( max <= p ) break;
}
len = p - position;
if( matchlen < len
|| ( matchlen == len && matchpos < scanpos ) ){
matchpos = scanpos;
matchlen = len;
if( max <= p ){
scanlimit = scanpos;
break;
}
}
}
scanpos = this.prev[ scanpos & ( this.DictionarySize - 1 ) ];
}
revbits++;
}
maxmatch = this.primaryHash.hashRequires() + requires - i - 1;
}
}// if( phashRequires < this.TextBuffer.length - position )
//------------------------------------------------------------------
// 最長一致を呼び出し元に返す。
if( this.Threshold <= matchlen ){
return LzssOutputStream.createSearchReturn( matchlen, matchpos );
}else{
return LzssOutputStream.NOMATCH;
}
}
/**
* TextBuffer内のpositionまでのデータを
* 前方へ移動する際、それに応じて SearchMethod内の
* データも TextBuffer内のデータと矛盾しないように
* 前方へ移動する処理を行う。
*/
public void slide(){
//------------------------------------------------------------------
// DictionaryLimit更新
this.DictionaryLimit = Math.max( 0, this.DictionaryLimit - this.DictionarySize );
//------------------------------------------------------------------
// primaryCount の値によって secondaryHashTable を再構成する
int secondaryIndex = 0;
int dummyIndex = 0;
for( int i = 0 ; i < this.primaryHashTable.length ; i++ ){
this.primaryHashTable[i] = dummyIndex;
int bits = this.secondaryHashRequires[i] * 2;
if( 1 << ( 5 + bits ) <= this.primaryCount[i] ){
for( int j = 0 ; j < ( 1 << bits ) ; j++ ){
this.divide( dummyIndex, secondaryIndex, this.primaryHash.hashRequires() + this.secondaryHashRequires[i] );
dummyIndex += 4;
secondaryIndex += 1;
}
this.secondaryHashRequires[i]++;
}else if( 0 < bits && this.primaryCount[i] < ( 1 << ( 2 + bits ) ) ){
for( int j = 0 ; j < ( 1 << ( bits - 2 ) ) ; j++ ){
this.merge( dummyIndex, secondaryIndex );
dummyIndex += 1;
secondaryIndex += 4;
}
this.secondaryHashRequires[i]--;
}else{
for( int j = 0 ; j < ( 1 << bits ) ; j++ ){
int pos = this.secondaryHashTable[ secondaryIndex++ ] - this.DictionarySize;
this.dummy[ dummyIndex++ ] = ( 0 <= pos ? pos : -1 );
}
}
this.primaryCount[i] = 0;
}
int[] temp = this.secondaryHashTable;
this.secondaryHashTable = this.dummy;
this.dummy = temp;
//------------------------------------------------------------------
// 連結リストを更新
for( int i = 0 ; i < this.prev.length ; i++ ){
int pos = this.prev[i] - this.DictionarySize;
this.prev[i] = ( 0 <= pos ? pos : -1 );
}
}
/**
* put() で LzssSearchMethodにデータを
* 登録するときに使用されるデータ量を得る。
* TwoLevelHashSearch では、内部で使用している HashMethod の実装が
* hash() のために必要とするデータ量( HashMethod.hashRequires() の戻り値 )
* と 二段目のハッシュに必要な最大のバイト数を足したものを返す。
*
* @return 一段目と二段目のハッシュに必要なバイト数を足したもの。
*/
public int putRequires(){
return this.primaryHash.hashRequires()
+ Math.max( Bits.len( this.DictionarySize ) - 5, 0 ) / 2;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// secondary hash method
//------------------------------------------------------------------
// private int secondaryHash( int position, int hashRequires )
//------------------------------------------------------------------
/**
* 二段目のハッシュ関数
*
* @param position TextBuffer内のデータパタンの開始位置
* @param hashRequires 二段目のハッシュ値を算出するのに必要なバイト数
*/
private int secondaryHash( int position, int hashRequires ){
int hash = 0;
int pos = position + this.primaryHash.hashRequires();
while( 0 < hashRequires-- ){
hash <<= 2;
hash |= this.TextBuffer[ pos++ ] & 0x03;
}
return hash;
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// divide and merge chained list
//------------------------------------------------------------------
// private void divide( int dbase, int sbase, int divoff )
// private void merge( int dbase, int sbase )
//------------------------------------------------------------------
/**
* 二段目のハッシュテーブルと連結リストを分岐させる。
*
* @param dbase 分岐先 this.dummy の index
* @param sbase 分岐元 this.secondaryHashTable の index
* @param divoff 分岐位置
*/
private void divide( int dbase, int sbase, int divoff ){
int limit = this.DictionarySize;
int position = this.secondaryHashTable[ sbase ];
int[] current = { -1, -1, -1, -1 };
//------------------------------------------------------------------
// 連結リストを分岐させていくループ
while( limit < position ){
int shash = this.TextBuffer[ position + divoff ] & 0x03;
if( 0 < current[ shash ] ){
this.prev[ current[ shash ] & ( this.DictionarySize - 1 ) ] = position;
}else{
this.dummy[ dbase + shash ] = position - this.DictionarySize;
}
current[ shash ] = position;
position = this.prev[ position & ( this.DictionarySize - 1 ) ];
}
//------------------------------------------------------------------
// 連結リストをターミネートする。
for( int i = 0 ; i < current.length ; i++ ){
if( 0 < current[ i ] ){
this.prev[ current[ i ] & ( this.DictionarySize - 1 ) ] = -1;
}else{
this.dummy[ dbase + i ] = -1;
}
}
}
/**
* 二段目のハッシュテーブルと連結リストを束ねる。
*
* @param dbase 分岐先 this.dummy の index
* @param sbase 分岐元 this.secondaryHashTable の index
*/
private void merge( int dbase, int sbase ){
int limit = this.DictionarySize;
int position = -1;
//------------------------------------------------------------------
// 連結リストを束ねていくループ
while( true ){
int shash = 0;
int max = this.secondaryHashTable[ sbase ];
for( int i = 1 ; i < 4 ; i++ ){
if( max < this.secondaryHashTable[ sbase + i ] ){
shash = i;
max = this.secondaryHashTable[ sbase + i ];
}
}
if( limit < max ){
this.secondaryHashTable[ sbase + shash ] =
this.prev[ max & ( this.DictionarySize - 1 ) ];
if( 0 < position ){
this.prev[ position & ( this.DictionarySize - 1 ) ] = max;
}else{
this.dummy[ dbase ] = max - this.DictionarySize;
}
position = max;
}else{
break;
}
}
//------------------------------------------------------------------
// 連結リストをターミネートする。
if( 0 < position ){
this.prev[ position & ( this.DictionarySize - 1 ) ] = -1;
}else{
this.dummy[ dbase ] = -1;
}
}
}
//end of TwoLevelHashSearch.java
jp/gr/java_conf/dangan/util/MsdosDate.java 100644 0 0 23346 7573513400 16123 0 ustar 0 0 //start of MsdosDate.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* MsdosDate.java
*
* Copyright (C) 2001-2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util;
//import classes and interfaces
import java.util.Date;
import java.lang.Cloneable;
//import exceptions
import java.lang.IllegalArgumentException;
/**
* MS-DOS形式の時間情報を扱うDateの派生クラス。
* データは 4byte値であり、MS-DOSが 主にIntel の x86系CPU上で
* 動作したことから LittleEndianで格納される。
* フォーマットは以下のとおり。
*
* +---------------+---------------++---------------+---------------+
* | 日付-上位byte | 日付-下位byte || 時刻-上位byte | 時刻-下位byte |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0||7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | 年-7bit |月-4bit| 日-5bit ||時間-5bit| 分-6bit | 秒-5bit |
* +-------------+-------+---------++---------+-----------+---------+
*
* ・年は 1980〜2107年 を 0〜127 で表す。WindowsのシステムAPIの
* 一部は 2099年までしかサポートしないという情報がある。
* ・月は 1〜12月 を 1〜12で表す。0〜11でないことに注意。
* ・日は 1〜31日 を 1〜31で表す。0〜30でないことに注意。
* ・時間は 0〜23時 を 0〜23で表す。
* ・分は 0〜59分 を 0〜59で表す。
* ・秒は 0〜58秒 を 0〜29で表す。秒の情報はビット数が足りない
* ため 最小単位は 1秒でなく 2秒である。
*
*
* -- revision history --
* $Log: MsdosDate.java,v $
* Revision 1.1 2002/12/05 00:00:00 dangan
* [maintenance]
* javadoc コメントのスペルミスを修正。
* ソース整備
*
* Revision 1.0 2002/07/24 00:00:00 dangan
* add to version control
* [bug fix]
* setTime() で ftimeの限界精度である2秒以上の精度で記録していた。
* [maintenance]
* タブの廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class MsdosDate extends Date
implements Cloneable {
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public MsdosDate( Date date )
// public MsdosDate( int time )
//------------------------------------------------------------------
/**
* date で示される時間を表す MsdosDate を構築する。
* MS-DOS 形式の時間情報で表せない細かい精度の情報は
* 無視され、最小時間単位は java.util.Date の 1ミリ秒でなく
* MS-DOS 形式の時間情報 の最小単位である 2秒となる。
*
* @param date 新しく構築される MsdosDate の基になる時間情報
* を持つ Dateオブジェクト
*
* @exception IllegalArgumentException
* date が MS-DOS時間形式で扱えない範囲の時間を
* 示していた場合
*/
public MsdosDate( Date date ){
super( ( date.getTime() / 2000L ) * 2000L );
this.checkRange();
}
/**
* MS-DOS 形式の時間情報から 新しい MsdosDate を構築
* する。
*
* @param time MS-DOS 形式の時間情報
*/
public MsdosDate( int time ){
super( ( ( time >> 25 ) & 0x7F ) + 80,
( ( time >> 21 ) & 0x0F ) - 1,
( time >> 16 ) & 0x1F,
( time >> 11 ) & 0x1F,
( time >> 5 ) & 0x3F,
( time << 1 ) & 0x3F ); //deprecated
this.checkRange();
}
//------------------------------------------------------------------
// method of java.lang.Cloneable
//------------------------------------------------------------------
// public Object clone()
//------------------------------------------------------------------
/**
* このオブジェクトのコピーを返す。
*
* @return このMsdosDateオブジェクトの複製
*/
public Object clone(){
return new MsdosDate( this );
}
//------------------------------------------------------------------
// method of java.util.Date
//------------------------------------------------------------------
// access method
//------------------------------------------------------------------
// set method with range check
//------------------------------------------------------------------
// public void setYear( int year )
// public void setTime( long time )
//------------------------------------------------------------------
/**
* この MsdosDate の示す年を year で指定された値に1900を足し
* たものに設定する。
* このメソッドは範囲チェックを行うだけのために存在する。
*
* @deprecated
* @param year 1900を足すことで西暦を表すような 年の値
*
* @exception IllegalArgumentException
* year が MS-DOS時間形式で扱えない範囲の時間を
* 示していた場合
*/
public void setYear( int year ){
if( year < 80 || 207 < year ){
throw new IllegalArgumentException( "out of MS-DOS time format range." );
}else{
super.setYear( year ); //deprecated
}
}
/**
* この MsdosDate の示す時間を 1970年1月1日 00:00:00 GMTから
* time ミリ秒経過した時刻に設定する。
* MS-DOS 形式の時間情報で表せない細かい精度の情報は
* 無視され、最小時間単位は java.util.Date の 1ミリ秒でなく
* MS-DOS 形式の時間情報 の最小単位である 2秒となる。
*
* @param time 1970年1月1日 00:00:00GMT からの経過ミリ秒
*
* @exception IllegalArgumentException
* time が MS-DOS時間形式で扱えない範囲の時間を
* 示していた場合
*/
public void setTime( long time ){
int year = ( new Date( time ) ).getYear();
if( year < 80 || 207 < year ){
throw new IllegalArgumentException( "out of MS-DOS time format range." );
}else{
super.setTime( ( time / 2000L ) * 2000L );
}
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// access method of MS-DOS time format
//------------------------------------------------------------------
// public void setMsdosTime( int time )
// public int getMsdosTime()
//------------------------------------------------------------------
/**
* この MsdosDate に MS-DOS 時間形式の時間情報を設定する。
*
* @param time MS-DOS 時間形式の時間情報
*/
public void setMsdosTime( int time ){
Date date = new Date( ( ( time >> 25 ) & 0x7F ) + 80,
( ( time >> 21 ) & 0x0F ) - 1,
( time >> 16 ) & 0x1F,
( time >> 11 ) & 0x1F,
( time >> 5 ) & 0x3F,
( time << 1 ) & 0x3F ); //deprecated
this.setTime( date.getTime() );
}
/**
* この MsdosDateが示す時間情報を MS-DOS 時間形式で得る。
*
* @return MS-DOS時間形式の値
*/
public int getMsdosTime(){
return ( ( super.getYear() - 80 ) << 25 ) //deprecated
| ( ( super.getMonth() + 1 ) << 21 ) //deprecated
| ( super.getDate() << 16 ) //deprecated
| ( super.getHours() << 11 ) //deprecated
| ( super.getMinutes() << 5 ) //deprecated
| ( super.getSeconds() >> 1 ); //deprecated
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private void checkRange()
//------------------------------------------------------------------
/**
* この MsdosDate が MS-DOS時間形式で表せる時間の範囲内で
* あるかを判定する。
*
* @exception IllegalArgumentException
* この MsdosDate が MS-DOS時間形式で扱えない
* 範囲の時間を示していた場合
*/
private void checkRange(){
int year = this.getYear();
if( year < 80 || 207 < year )
throw new IllegalArgumentException( "out of MS-DOS time format range." );
}
}
//end of MsdosDate.java
jp/gr/java_conf/dangan/util/WindowsDate.java 100644 0 0 33134 7573513400 16464 0 ustar 0 0 //start of WindowsDate.java
//TEXT_STYLE:CODE=Shift_JIS(Japanese):RET_CODE=CRLF
/**
* WindowsDate.java
*
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package jp.gr.java_conf.dangan.util;
//import classes and interfaces
import java.util.Date;
import java.lang.Cloneable;
//import exceptions
import java.lang.IllegalArgumentException;
/**
* WindowsのFILETIME形式の情報を扱うDateの派生クラス。
* FILETIME は 1601年 1月 1日 0時0分0秒からの経過時間を
* 100ナノ秒単位で持つ64ビット値。
* このクラスでは FILETIME を long(64ビット値)として扱うときは
* 基本的に符号無しとみなす。
* 1601年 1月 1日 0時0分0秒以前の時間を扱いたい場合は
* WindowsDate( Date date ) か、WindowsDate.setTime( long time )を使用する。
*
*
* -- revision history --
* $Log: WindowsDate.java,v $
* Revision 1.1 2002/12/05 00:00:00 dangan
* [maintenance]
* javadoc コメントのスペルミスを修正。
* ソース整備
*
* Revision 1.0 2002/08/05 00:00:00 dangan
* add to version control
* [bug fix]
* set系メソッドで 範囲外の時間をセットしようとして
* 例外を投げるケースで時間の書き戻しが正しく行われていなかった。
* checkRange の時間の範囲が間違っていた。
* [maintenance]
* タブの廃止
* ライセンス文の修正
*
*
*
* @author $Author: dangan $
* @version $Revision: 1.1 $
*/
public class WindowsDate extends Date
implements Cloneable{
//------------------------------------------------------------------
// class field
//------------------------------------------------------------------
// public static final long TIME_DIFFERENCE
//------------------------------------------------------------------
/**
* FILETIME形式のデータと、java.util.Date.getTime() で
* 得られる時間形式との時間差を 100ナノセカンド単位で示した数値。
* なお、閏秒等は考慮に入れていない。
*/
public static final long TIME_DIFFERENCE = 0x19DB1DED53E8000L;
//------------------------------------------------------------------
// instance field
//------------------------------------------------------------------
// private int NanoSecounds
//------------------------------------------------------------------
/**
* java.util.Date では保持できない
* ナノ秒単位の時間を保持するために用いる。
*/
private int NanoSecounds;
//------------------------------------------------------------------
// constructor
//------------------------------------------------------------------
// public WindowsDate()
// public WindowsDate( Date date )
// public WindowsDate( long time )
//------------------------------------------------------------------
/**
* デフォルトコンストラクタ。
* 現在の時間情報を持つ WindowsDateを構築する。
* ナノ秒単位の時間は取得できないため、0に設定される。
*
* @exception IllegalArgumentException
* 現在の時間が FILETIME 形式で表現できる
* 範囲外だった場合。
*/
public WindowsDate(){
super();
this.NanoSecounds = 0;
this.checkRange();
}
/**
* dateで示される時間を表す WindowsDateを構築する。
* dateが WindowsDate のインスタンスならば
* ナノ秒単位の情報もコピーされるが、それ以外の場合は
* ナノ秒単位の情報には 0 が設定される。
*
* @param date 新しく構築される WindowsDate の元となる時間情報を持つ
* Date のオブジェクト
*
* @exception IllegalArgumentException
* 現在の時間が FILETIME 形式で表現できる
* 範囲外だった場合。
*/
public WindowsDate( Date date ){
super( date.getTime() );
if( date instanceof WindowsDate ){
this.NanoSecounds = ((WindowsDate)date).NanoSecounds;
}else{
this.NanoSecounds = 0;
this.checkRange();
}
}
/**
* 符号無し64ビットのFILETIME形式の時間情報から
* 新しいWindowsDateを構築する。
*
* @param time FILETIME形式の時間情報
*/
public WindowsDate( long time ){
super( 0 <= time
? ( time - WindowsDate.TIME_DIFFERENCE ) / 10000L
: ( ( time >>> 1 ) - ( WindowsDate.TIME_DIFFERENCE >>> 1 ) ) / 5000L );
this.NanoSecounds =
(int)( ( time >>> 1 ) % 5000L * 2 + ( time & 1 ) ) * 100;
}
//------------------------------------------------------------------
// method of java.lang.Cloneable
//------------------------------------------------------------------
// public Object clone()
//------------------------------------------------------------------
/**
* このオブジェクトのコピーを返す。
*
* @return このWindowsDateオブジェクトの複製
*/
public Object clone(){
return new WindowsDate( this );
}
//------------------------------------------------------------------
// method of java.util.Date
//------------------------------------------------------------------
// set method with range check
//------------------------------------------------------------------
// public void setYear( int year )
// public void setMonth( int month )
// public void setDate( int day )
// public void setHours( int hour )
// public void setMinutes( int minute )
// public void setSecounds( int secound )
// public void setTime( long time )
//------------------------------------------------------------------
/**
* この WindowsDate の示す年を year で
* 指定された値に1900を足したものに設定する。
* このメソッドは範囲チェックを行うだけのために存在する。
*
* @param year 1900を足すことで西暦を表すような 年の値
*
* @exception IllegalArgumentException
* year に変更したところ FILETIME形式で扱えない
* 範囲の時間になった場合
* @deprecated
*/
public void setYear( int year ){
long temp = this.getTime();
try{
super.setYear( year );
this.checkRange();
}catch( IllegalArgumentException exception ){
this.setTime( temp );
throw exception;
}
}
/**
* この WindowsDate の示す月を month で指定された値に設定する。
* このメソッドは範囲チェックを行うだけのために存在する。
*
* @param month 0が1月、1が2月を示すような月の値
*
* @exception IllegalArgumentException
* month に変更したところ FILETIME形式で扱えない
* 範囲の時間になった場合
* @deprecated
*/
public void setMonth( int month ){
long temp = this.getTime();
try{
super.setMonth( month );
this.checkRange();
}catch( IllegalArgumentException exception ){
this.setTime( temp );
throw exception;
}
}
/**
* この WindowsDate の示す 一ヶ月の
* 中での何日目かを date で指定された値に設定する。
* このメソッドは範囲チェックを行うだけのために存在する。
*
* @param date 1が1日、2が2日を示すような日の値
*
* @exception IllegalArgumentException
* date に変更したところ FILETIME形式で扱えない
* 範囲の時間になった場合
* @deprecated
*/
public void setDate( int date ){
long temp = this.getTime();
try{
super.setDate( date );
this.checkRange();
}catch( IllegalArgumentException exception ){
this.setTime( temp );
throw exception;
}
}
/**
* この WindowsDate の示す一日の中での時間を
* hours で指定された値に設定する。
* このメソッドは範囲チェックを行うだけのために存在する。
*
* @param hours 時間の値
*
* @exception IllegalArgumentException
* hours に変更したところ FILETIME形式で扱えない
* 範囲の時間になった場合
* @deprecated
*/
public void setHours( int hours ){
long temp = this.getTime();
try{
super.setHours( hours );
this.checkRange();
}catch( IllegalArgumentException exception ){
this.setTime( temp );
throw exception;
}
}
/**
* この WindowsDate の示す一時間の中での分を
* minutes で指定された値に設定する。
* このメソッドは範囲チェックを行うだけのために存在する。
*
* @param minutes 分の値
*
* @exception IllegalArgumentException
* minutes に変更したところ FILETIME形式で扱えない
* 範囲の時間になった場合
* @deprecated
*/
public void setMinutes( int minutes ){
long temp = this.getTime();
try{
super.setMinutes( minutes );
this.checkRange();
}catch( IllegalArgumentException exception ){
this.setTime( temp );
throw exception;
}
}
/**
* この WindowsDate の示す一分の中での秒数を
* secounds で指定された値に設定する。
* このメソッドは範囲チェックを行うだけのために存在する。
*
* @param secounds 秒数
*
* @exception IllegalArgumentException
* secounds に変更したところ FILETIME形式で扱えない
* 範囲の時間になった場合
* @deprecated
*/
public void setSeconds( int seconds ){
long temp = this.getTime();
try{
super.setSeconds( seconds );
this.checkRange();
}catch( IllegalArgumentException exception ){
this.setTime( temp );
throw exception;
}
}
/**
* この WindowsDate の示す時間を
* 1970年1月1日 00:00:00 GMTから
* time ミリ秒経過した時刻に設定する。
* このメソッドは範囲チェックを行うだけのために存在する。
*
* @param time 1970年1月1日 00:00:00GMT からの経過ミリ秒
*
* @exception IllegalArgumentException
* time がFILETIME形式で扱えない
* 範囲の時間を示していた場合
*/
public void setTime( long time ){
long temp = this.getTime();
try{
super.setTime( time );
this.checkRange();
}catch( IllegalArgumentException exception ){
this.setTime( temp );
throw exception;
}
}
//------------------------------------------------------------------
// original method
//------------------------------------------------------------------
// access method with FILETIME format
//------------------------------------------------------------------
// public void setWindowsTime( long time )
// public long getWindowsTime()
//------------------------------------------------------------------
/**
* この WindowsDate に FILETIME形式の時間情報を設定する。
*
* @param time FILETIME形式の時間情報
*/
public void setWindowsTime( long time ){
super.setTime( 0 <= time
? ( time - WindowsDate.TIME_DIFFERENCE ) / 10000L
: ( ( time >>> 1 ) - ( WindowsDate.TIME_DIFFERENCE >>> 1 ) ) / 5000L );
this.NanoSecounds =
(int)( ( time >>> 1 ) % 5000L * 2 + ( time & 1 ) ) * 100;
}
/**
* この WindowsDateが示す時間情報を FILETIME 形式で得る。
*
* @return FILETIME形式の値
*/
public long getWindowsTime() {
return ( super.getTime() * 10000L + WindowsDate.TIME_DIFFERENCE
+ (long)( this.NanoSecounds / 100 ) );
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private void checkRange()
//------------------------------------------------------------------
/**
* この WindowsDate が FILETIME形式で表せる時間の
* 範囲内であるかを判定する。まだ不完全
*
* @exception IllegalArgumentException
* この WindowsDate が FILETIME形式で扱えない
* 範囲の時間を示していた場合
*/
private void checkRange(){
long time = super.getTime();
if( !( 0xFFFCAE8C71E60F9BL <= time && time <= 0x000683218A10A8CBL ) )
throw new IllegalArgumentException( "outside of range of Windows FILETIME format. " );
}
}
lists/classfiles.all.list 100644 0 0 5731 7575723657 13154 0 ustar 0 0 LHA.class
jp/gr/java_conf/dangan/io/Bits.class
jp/gr/java_conf/dangan/io/LittleEndian.class
jp/gr/java_conf/dangan/io/BitInputStream*.class
jp/gr/java_conf/dangan/io/BitOutputStream.class
jp/gr/java_conf/dangan/io/GrowthByteBuffer.class
jp/gr/java_conf/dangan/io/CachedInputStream.class
jp/gr/java_conf/dangan/io/LimitedInputStream.class
jp/gr/java_conf/dangan/io/Disconnectable.class
jp/gr/java_conf/dangan/io/DisconnectableInputStream.class
jp/gr/java_conf/dangan/io/DisconnectableOutputStream.class
jp/gr/java_conf/dangan/io/BitDataBrokenException.class
jp/gr/java_conf/dangan/io/NotEnoughBitsException.class
jp/gr/java_conf/dangan/util/MsdosDate.class
jp/gr/java_conf/dangan/util/WindowsDate.class
jp/gr/java_conf/dangan/util/lha/CRC16.class
jp/gr/java_conf/dangan/util/lha/LhaFile*.class
jp/gr/java_conf/dangan/util/lha/LhaHeader.class
jp/gr/java_conf/dangan/util/lha/HashMethod.class
jp/gr/java_conf/dangan/util/lha/HashShort.class
jp/gr/java_conf/dangan/util/lha/HashShort2.class
jp/gr/java_conf/dangan/util/lha/HashDefault.class
jp/gr/java_conf/dangan/util/lha/LhaChecksum.class
jp/gr/java_conf/dangan/util/lha/LhaConstants.class
jp/gr/java_conf/dangan/util/lha/StaticHuffman.class
jp/gr/java_conf/dangan/util/lha/DynamicHuffman.class
jp/gr/java_conf/dangan/util/lha/LhaInputStream.class
jp/gr/java_conf/dangan/util/lha/LhaOutputStream*.class
jp/gr/java_conf/dangan/util/lha/LhaImmediateOutputStream*.class
jp/gr/java_conf/dangan/util/lha/LhaRetainedOutputStream*.class
jp/gr/java_conf/dangan/util/lha/LookUpTableOfFirstByte.class
jp/gr/java_conf/dangan/util/lha/LzssInputStream.class
jp/gr/java_conf/dangan/util/lha/LzssOutputStream.class
jp/gr/java_conf/dangan/util/lha/PreLh5Decoder*.class
jp/gr/java_conf/dangan/util/lha/PreLh3Decoder.class
jp/gr/java_conf/dangan/util/lha/PreLh2Decoder.class
jp/gr/java_conf/dangan/util/lha/PreLh1Decoder.class
jp/gr/java_conf/dangan/util/lha/PreLz5Decoder.class
jp/gr/java_conf/dangan/util/lha/PreLzsDecoder.class
jp/gr/java_conf/dangan/util/lha/PreLzssDecoder.class
jp/gr/java_conf/dangan/util/lha/PostLh5Encoder.class
jp/gr/java_conf/dangan/util/lha/PostLh3Encoder.class
jp/gr/java_conf/dangan/util/lha/PostLh2Encoder.class
jp/gr/java_conf/dangan/util/lha/PostLh1Encoder.class
jp/gr/java_conf/dangan/util/lha/PostLz5Encoder.class
jp/gr/java_conf/dangan/util/lha/PostLzsEncoder.class
jp/gr/java_conf/dangan/util/lha/PostLzssEncoder.class
jp/gr/java_conf/dangan/util/lha/LzssSearchMethod.class
jp/gr/java_conf/dangan/util/lha/SimpleSearch.class
jp/gr/java_conf/dangan/util/lha/BinaryTreeSearch.class
jp/gr/java_conf/dangan/util/lha/HashAndBinaryTreeSearch.class
jp/gr/java_conf/dangan/util/lha/HashAndChainedListSearch.class
jp/gr/java_conf/dangan/util/lha/PatriciaTrieSearch.class
jp/gr/java_conf/dangan/util/lha/TwoLevelHashSearch.class
jp/gr/java_conf/dangan/util/lha/BadHuffmanTableException.class
jp/gr/java_conf/dangan/util/lha/LhaRetainedOutputStream*.class
jp/gr/java_conf/dangan/util/lha/LhaImmediateOutputStream*.class
lists/srcfiles.deprecation.list 100644 0 0 132 7575723705 14323 0 ustar 0 0 jp/gr/java_conf/dangan/util/MsdosDate.java
jp/gr/java_conf/dangan/util/WindowsDate.java
lists/srcfiles.nodeprecation.list 100644 0 0 5516 7576167157 14717 0 ustar 0 0 LHA.java
jp/gr/java_conf/dangan/io/BitDataBrokenException.java
jp/gr/java_conf/dangan/io/Bits.java
jp/gr/java_conf/dangan/io/BitInputStream.java
jp/gr/java_conf/dangan/io/BitOutputStream.java
jp/gr/java_conf/dangan/io/CachedInputStream.java
jp/gr/java_conf/dangan/io/Disconnectable.java
jp/gr/java_conf/dangan/io/DisconnectableInputStream.java
jp/gr/java_conf/dangan/io/DisconnectableOutputStream.java
jp/gr/java_conf/dangan/io/GrowthByteBuffer.java
jp/gr/java_conf/dangan/io/LimitedInputStream.java
jp/gr/java_conf/dangan/io/LittleEndian.java
jp/gr/java_conf/dangan/io/NotEnoughBitsException.java
jp/gr/java_conf/dangan/lang/reflect/Factory.java
jp/gr/java_conf/dangan/lang/reflect/MethodUtil.java
jp/gr/java_conf/dangan/lang/reflect/Type.java
jp/gr/java_conf/dangan/util/lha/BadHuffmanTableException.java
jp/gr/java_conf/dangan/util/lha/BinaryTreeSearch.java
jp/gr/java_conf/dangan/util/lha/CRC16.java
jp/gr/java_conf/dangan/util/lha/CompressMethod.java
jp/gr/java_conf/dangan/util/lha/DynamicHuffman.java
jp/gr/java_conf/dangan/util/lha/HashAndBinaryTreeSearch.java
jp/gr/java_conf/dangan/util/lha/HashAndChainedListSearch.java
jp/gr/java_conf/dangan/util/lha/HashDefault.java
jp/gr/java_conf/dangan/util/lha/HashMethod.java
jp/gr/java_conf/dangan/util/lha/HashShort.java
jp/gr/java_conf/dangan/util/lha/LhaFile.java
jp/gr/java_conf/dangan/util/lha/LhaChecksum.java
jp/gr/java_conf/dangan/util/lha/LhaConstants.java
jp/gr/java_conf/dangan/util/lha/LhaHeader.java
jp/gr/java_conf/dangan/util/lha/LhaImmediateOutputStream.java
jp/gr/java_conf/dangan/util/lha/LhaInputStream.java
jp/gr/java_conf/dangan/util/lha/LhaOutputStream.java
jp/gr/java_conf/dangan/util/lha/LhaProperty.java
jp/gr/java_conf/dangan/util/lha/LhaRetainedOutputStream.java
jp/gr/java_conf/dangan/util/lha/LzssInputStream.java
jp/gr/java_conf/dangan/util/lha/LzssOutputStream.java
jp/gr/java_conf/dangan/util/lha/LzssSearchMethod.java
jp/gr/java_conf/dangan/util/lha/PatriciaTrieSearch.java
jp/gr/java_conf/dangan/util/lha/PreLh5Decoder.java
jp/gr/java_conf/dangan/util/lha/PreLh3Decoder.java
jp/gr/java_conf/dangan/util/lha/PreLh2Decoder.java
jp/gr/java_conf/dangan/util/lha/PreLh1Decoder.java
jp/gr/java_conf/dangan/util/lha/PreLz5Decoder.java
jp/gr/java_conf/dangan/util/lha/PreLzsDecoder.java
jp/gr/java_conf/dangan/util/lha/PreLzssDecoder.java
jp/gr/java_conf/dangan/util/lha/PostLh5Encoder.java
jp/gr/java_conf/dangan/util/lha/PostLh3Encoder.java
jp/gr/java_conf/dangan/util/lha/PostLh2Encoder.java
jp/gr/java_conf/dangan/util/lha/PostLh1Encoder.java
jp/gr/java_conf/dangan/util/lha/PostLz5Encoder.java
jp/gr/java_conf/dangan/util/lha/PostLzsEncoder.java
jp/gr/java_conf/dangan/util/lha/PostLzssEncoder.java
jp/gr/java_conf/dangan/util/lha/SimpleSearch.java
jp/gr/java_conf/dangan/util/lha/StaticHuffman.java
jp/gr/java_conf/dangan/util/lha/TwoLevelHashSearch.java **
* この WindowsDateが示す時間情報を FILETIME 形式で得る。
*
* @return FILETIME形式の値
*/
public long getWindowsTime() {
return ( super.getTime() * 10000L + WindowsDate.TIME_DIFFERENCE
+ (long)( this.NanoSecounds / 100 ) );
}
//------------------------------------------------------------------
// local method
//------------------------------------------------------------------
// private void checkRange()
//------------------------------------------------------------------
/**
* この WindowsDate が FILETIME形式で表せる時間の
* 範囲内であるかを判定する。まだ不完全
*
* @exception IllegalArgumentException
* この WindowsDate が FILETIME形式で扱えない
* 範囲の時間を示していた場合
*/
private void checkRange(){
long time = super.getTime();
if( !( 0xFFFCAE8C71E60F9BL <= time && time <= 0x000683218A10A8CBL ) )
throw new IllegalArgumentException( "outside of range of Windows FILETIME format. " );
}
}
lists/classfiles.all.list 100644 0 0 5731 7575723657 13154 0 ustar 0 0 LHA.class
jp/gr/java_conf/dangan/io/Bits.class
jp/gr/java_conf/dangan/io/LittleEndian.class
jp/gr/java_conf/dangan/io/BitInputStream*.class
jp/gr/java_conf/dangan/io/BitOutputStream.class
jp/gr/java_conf/dangan/io/GrowthByteBuffer.class
jp/gr/java_conf/dangan/io/CachedInputStream.class
jp/gr/java_conf/dangan/io/LimitedInputStream.class
jp/gr/java_conf/dangan/io/Disconnectable.class
jp/gr/java_conf/dangan/io/DisconnectableInputStream.class
jp/gr/java_conf/dangan/io/DisconnectableOutputStream.class