![JAR search and dependency download from the Maven repository](/logo.png)
com.orangesignal.jlha.PostLh5Encoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of orangesignal-csv Show documentation
Show all versions of orangesignal-csv Show documentation
OrangeSignal CSV is a very flexible csv (comma-separated values) read and write library for Java.
/**
* Copyright (C) 2002 Michel Ishizuka All rights reserved.
*
* 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
* 変更の有無にかかわらず許可する。
*
* 1.ソースコードの再配布において著作権表示と この条件のリスト
* および下記の声明文を保持しなくてはならない。
*
* 2.バイナリ形式の再配布において著作権表示と この条件のリスト
* および下記の声明文を使用説明書もしくは その他の配布物内に
* 含む資料に記述しなければならない。
*
* このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
* 的を達成できるという保証、商品価値が有るという保証にとどまらず、
* いかなる明示的および暗示的な保証もしない。
* 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
* 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
* 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
* サービスの導入費等が考えられるが、決してそれだけに限定されない
* 損害)に対して、いかなる事態の原因となったとしても、契約上の責
* 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
* 正行為のためであったとしても、またはそのような損害の可能性が報
* 告されていたとしても一切の責任を負わないものとする。
*/
package com.orangesignal.jlha;
import java.io.IOException;
import java.io.OutputStream;
/**
* -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 {
/**
* -lh4-, -lh5-, -lh6-, -lh7- 形式の圧縮データの出力先の ビット出力ストリーム
*/
private BitOutputStream out;
/**
* LZSSの辞書サイズ
*/
private int dictionarySize;
/**
* LZSSの最大一致長
*/
private int maxMatch;
/**
* LZSS 圧縮/非圧縮 の閾値
*/
private int threshold;
/**
* 辞書サイズを示すのに必要なバイト数
*/
private int dictionarySizeByteLen;
/**
* this.block[ this.currentBlock ] 内の現在処理位置
*/
private int position;
/**
* flag バイト内の圧縮/非圧縮を示すフラグ
*/
private int flagBit;
/**
* this.block[ this.currentBlock ] 内の flagバイトの位置
*/
private int flagPos;
/**
* 現在処理中のハフマンブロックを示す。
*/
private int currentBlock;
/**
* ハフマンコード格納用バッファ群
*/
private byte[][] block;
/**
* 各ブロックの code データの数
*/
private int[] blockSize;
/**
* 該当するブロックの code 部分の頻度表を持つ頻度表群
*/
private int[][] blockCodeFreq;
/**
* 該当するブロックの offLen 部分の頻度表を持つ頻度表群
*/
private int[][] blockOffLenFreq;
/**
* 全ブロックを幾つかのグループに分割するパターンの配列。
*/
private int[][] pattern;
/**
* 複数ブロックを組み合わせたグループの配列。 this.group[0] 全ブロックを持つグループが this.group[1] this.group[2] には 全ブロックから各々最後と最初のブロックを欠いたグループが …というようにピラミッド状に構成される。
*/
private int[][] group;
/**
* -lh5- 圧縮用 PostLzssEncoder を構築する。
* バッファサイズにはデフォルト値が使用される。
*
* @param out 圧縮データを受け取る OutputStream
*/
public PostLh5Encoder(final 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(final OutputStream out, final 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(final OutputStream out, final String method, final 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(final OutputStream out, final String method, final int BlockNum, final int BlockSize, final int DivideNum) {
if (CompressMethod.LH4.equals(method) || CompressMethod.LH5.equals(method) || CompressMethod.LH6.equals(method) || CompressMethod.LH7.equals(method)) {
dictionarySize = CompressMethod.toDictionarySize(method);
maxMatch = CompressMethod.toMaxMatch(method);
threshold = CompressMethod.toThreshold(method);
dictionarySizeByteLen = (Bits.len(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);
}
currentBlock = 0;
block = new byte[BlockNum][];
blockSize = new int[BlockNum];
blockCodeFreq = new int[BlockNum][];
blockOffLenFreq = new int[BlockNum][];
final int codeFreqSize = 256 + maxMatch - threshold + 1;
final int offLenFreqSize = Bits.len(dictionarySize);
for (int i = 0; i < BlockNum; i++) {
block[i] = new byte[BlockSize];
blockCodeFreq[i] = new int[codeFreqSize];
blockOffLenFreq[i] = new int[offLenFreqSize];
}
group = createGroup(BlockNum, DivideNum);
pattern = createPattern(BlockNum, DivideNum);
position = 0;
flagBit = 0;
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
/**
* 1byte の LZSS未圧縮のデータもしくは、 LZSS で圧縮された圧縮コードのうち一致長を書きこむ。
*
* @param code 1byte の LZSS未圧縮のデータもしくは、 LZSS で圧縮された圧縮コードのうち一致長
* @exception IOException 入出力エラーが発生した場合
*/
@Override
public void writeCode(final int code) throws IOException {
final int need = (0x100 <= code ? dictionarySizeByteLen + 1 : 1) + (flagBit == 0 ? 1 : 0);
if (block[currentBlock].length - position < need || 65535 <= blockSize[currentBlock]) {
currentBlock++;
if (block.length <= currentBlock) {
writeOut();
} else {
position = 0;
}
flagBit = 0x80;
flagPos = position++;
block[currentBlock][flagPos] = 0;
} else if (flagBit == 0) {
flagBit = 0x80;
flagPos = position++;
block[currentBlock][flagPos] = 0;
}
// データ格納
block[currentBlock][position++] = (byte) code;
// 上位1ビットをフラグとして格納
if (0x100 <= code) {
block[currentBlock][flagPos] |= flagBit;
}
flagBit >>= 1;
// 頻度表更新
blockCodeFreq[currentBlock][code]++;
// ブロックサイズ更新
blockSize[currentBlock]++;
}
/**
* LZSS で圧縮された圧縮コードのうち一致位置を書きこむ。
*
* @param offset LZSS で圧縮された圧縮コードのうち一致位置
*/
@Override
public void writeOffset(final int offset) {
// データ格納
int shift = dictionarySizeByteLen - 1 << 3;
while (0 <= shift) {
block[currentBlock][position++] = (byte) (offset >> shift);
shift -= 8;
}
// 頻度表更新
blockOffLenFreq[currentBlock][Bits.len(offset)]++;
}
/**
* この PostLzssEncoder にバッファリングされている全ての 8ビット単位のデータを出力先の OutputStream に出力し、 出力先の OutputStream を flush() する。
* このメソッドは圧縮率を変化させる。
*
* @exception IOException 入出力エラーが発生した場合
* @see PostLzssEncoder#flush()
* @see BitOutputStream#flush()
*/
@Override
public void flush() throws IOException {
writeOut();
out.flush();
}
/**
* この出力ストリームと、接続された出力ストリームを閉じ、 使用していたリソースを開放する。
*
* @exception IOException 入出力エラーが発生した場合
*/
@Override
public void close() throws IOException {
writeOut(); // throws IOException
out.close(); // throws IOException
out = null;
block = null;
blockCodeFreq = null;
blockOffLenFreq = null;
group = null;
pattern = null;
}
/**
* この PostLh5Encoder が扱うLZSS辞書のサイズを得る。
*
* @return この PostLh5Encoder が扱うLZSS辞書のサイズ
*/
@Override
public int getDictionarySize() {
return dictionarySize;
}
/**
* この PostLh5Encoder が扱うLZSSの最長一致長を得る。
*
* @return この PostLh5Encoder が扱うLZSSの最大一致長
*/
@Override
public int getMaxMatch() {
return maxMatch;
}
/**
* この PostLh5Encoder が扱うLZSSの圧縮、非圧縮の閾値を得る。
*
* @return この PostLh5Encoder が扱うLZSSの圧縮、非圧縮の閾値
*/
@Override
public int getThreshold() {
return threshold;
}
// ------------------------------------------------------------------
// local method
/**
* バッファリングされた全てのデータを this.out に出力する。
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOut() throws IOException {
if (1 < block.length) {
writeOutBestPattern();
} else {
writeOutGroup(new int[] { 0 });
currentBlock = 0;
}
position = 0;
flagBit = 0;
}
/**
* バッファリングされた全てのデータを最良の構成で this.out に出力する。
*
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOutBestPattern() throws IOException {
int[] bestPattern = null;
final int[] groupHuffLen = new int[group.length];
// ------------------------------------------------------------------
// group を出力したときの bit 数を求める。
for (int i = 0; i < group.length; i++) {
if (group != null) {
int blockSize = 0;
for (int j = 0; j < group[i].length; j++) {
blockSize += this.blockSize[group[i][j]];
}
if (0 < blockSize && blockSize < 65536) {
groupHuffLen[i] = calcHuffmanCodeLength(dictionarySize, margeArrays(group[i], blockCodeFreq), margeArrays(group[i], blockOffLenFreq));
} else if (0 == blockSize) {
groupHuffLen[i] = 0;
} else {
groupHuffLen[i] = -1;
}
} else {
groupHuffLen[i] = -1;
}
}
// ------------------------------------------------------------------
// 出力 bit 数が最小となる pattern を総当りで求める。
int smallest = Integer.MAX_VALUE;
for (final int[] element : pattern) {
int length = 0;
for (int j = 0; j < element.length; j++) {
if (0 <= groupHuffLen[element[j]]) {
length += groupHuffLen[element[j]];
} else {
length = Integer.MAX_VALUE;
break;
}
}
if (length < smallest) {
bestPattern = element;
smallest = length;
}
}
// ------------------------------------------------------------------
// 最も出力 bit 数の少ないパターンで出力
// どの パターン もブロックサイズが 65536 以上の
// グループを持つ場合はブロック単位で出力。
if (bestPattern != null) {
for (final int element : bestPattern) {
writeOutGroup(group[element]); // throws IOException
}
} else {
for (int i = 0; i < block.length; i++) {
writeOutGroup(new int[] { i });
}
}
currentBlock = 0;
}
/**
* group で指定された ブロック群をハフマン符号化して this.out に出力する。
*
* @param group 出力するブロック番号を持つ配列
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOutGroup(final int[] group) throws IOException {
int[] codeFreq = margeArrays(group, blockCodeFreq);
int[] offLenFreq = margeArrays(group, blockOffLenFreq);
int blockSize = 0;
for (final int element : group) {
blockSize += this.blockSize[element];
}
if (0 < blockSize) {
// ------------------------------------------------------------------
// ブロックサイズ出力
out.writeBits(16, blockSize);
// ------------------------------------------------------------------
// ハフマン符号表生成
final int[] codeLen = StaticHuffman.FreqListToLenList(codeFreq);
final int[] codeCode = StaticHuffman.LenListToCodeList(codeLen);
final int[] offLenLen = StaticHuffman.FreqListToLenList(offLenFreq);
final int[] offLenCode = StaticHuffman.LenListToCodeList(offLenLen);
// ------------------------------------------------------------------
// code 部のハフマン符号表出力
if (2 <= countNoZeroElement(codeFreq)) {
final int[] codeLenFreq = createCodeLenFreq(codeLen);
final int[] codeLenLen = StaticHuffman.FreqListToLenList(codeLenFreq);
final int[] codeLenCode = StaticHuffman.LenListToCodeList(codeLenLen);
if (2 <= countNoZeroElement(codeLenFreq)) {
writeCodeLenLen(codeLenLen); // throws IOException
} else {
out.writeBits(5, 0); // throws IOException
out.writeBits(5, getNoZeroElementIndex(codeLenFreq));// throws IOException
}
writeCodeLen(codeLen, codeLenLen, codeLenCode); // throws IOException
} else {
out.writeBits(10, 0); // throws IOException
out.writeBits(18, getNoZeroElementIndex(codeFreq));// throws IOException
}
// ------------------------------------------------------------------
// offLen 部のハフマン符号表出力
if (2 <= countNoZeroElement(offLenFreq)) {
writeOffLenLen(offLenLen); // throws IOException
} else {
final int len = Bits.len(Bits.len(dictionarySize));
out.writeBits(len, 0); // throws IOException
out.writeBits(len, getNoZeroElementIndex(offLenFreq));// throws IOException
}
// ------------------------------------------------------------------
// ハフマン符号出力
for (final int element : group) {
position = 0;
flagBit = 0;
final byte[] buffer = block[element];
for (int j = 0; j < this.blockSize[element]; j++) {
if (flagBit == 0) {
flagBit = 0x80;
flagPos = position++;
}
if (0 == (buffer[flagPos] & flagBit)) {
final int code = buffer[position++] & 0xFF;
out.writeBits(codeLen[code], codeCode[code]); // throws IOException
} else {
final int code = buffer[position++] & 0xFF | 0x100;
int offset = 0;
for (int k = 0; k < dictionarySizeByteLen; k++) {
offset = offset << 8 | buffer[position++] & 0xFF;
}
final int offlen = Bits.len(offset);
out.writeBits(codeLen[code], codeCode[code]); // throws IOException
out.writeBits(offLenLen[offlen], offLenCode[offlen]); // throws IOException
if (1 < offlen) {
out.writeBits(offlen - 1, offset); // throws IOException
}
}
flagBit >>= 1;
}
}
// ------------------------------------------------------------------
// 次のブロックのための処理
for (int i = 0; i < group.length; i++) {
this.blockSize[group[i]] = 0;
codeFreq = blockCodeFreq[group[i]];
for (int j = 0; j < codeFreq.length; j++) {
codeFreq[j] = 0;
}
offLenFreq = blockOffLenFreq[group[i]];
for (int j = 0; j < offLenFreq.length; j++) {
offLenFreq[j] = 0;
}
}
}// if( 0 < blockSize )
}
/**
* codeLen の ハフマン符号長のリストを書き出す。
*
* @param codeLenLen codeLenFreq のハフマン符号長のリスト
* @exception IOException 入出力エラーが発生した場合
*/
private void writeCodeLenLen(final int[] codeLenLen) throws IOException {
int end = codeLenLen.length;
while (0 < end && codeLenLen[end - 1] == 0) {
end--;
}
out.writeBits(5, end); // throws IOException
int index = 0;
while (index < end) {
final int len = codeLenLen[index++];
if (len <= 6) {
out.writeBits(3, len); // throws IOException
} else {
out.writeBits(len - 3, (1 << len - 3) - 2);// throws IOException
}
if (index == 3) {
while (codeLenLen[index] == 0 && index < 6) {
index++;
}
out.writeBits(2, index - 3 & 0x03); // throws IOException
}
}
}
/**
* code 部のハフマン符号長のリストを ハフマンとランレングスで符号化しながら書き出す。
*
* @param codeLen codeFreq のハフマン符号長のリスト
* @param codeLenLen codeLenFreq のハフマン符号長のリスト
* @param codeLenCode codeLenFreq のハフマン符号のリスト
* @exception IOException 入出力エラーが発生した場合
*/
private void writeCodeLen(final int[] codeLen, final int[] codeLenLen, final int[] codeLenCode) throws IOException {
int end = codeLen.length;
while (0 < end && codeLen[end - 1] == 0) {
end--;
}
out.writeBits(9, end); // throws IOException
int index = 0;
while (index < end) {
final int len = codeLen[index++];
if (0 < len) {
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++) {
out.writeBits(codeLenLen[0], codeLenCode[0]); // throws IOException
}
} else if (count <= 18) {
out.writeBits(codeLenLen[1], codeLenCode[1]); // throws IOException
out.writeBits(4, count - 3); // throws IOException
} else if (count == 19) {
out.writeBits(codeLenLen[0], codeLenCode[0]); // throws IOException
out.writeBits(codeLenLen[1], codeLenCode[1]); // throws IOException
out.writeBits(4, 0x0F); // throws IOException
} else {
out.writeBits(codeLenLen[2], codeLenCode[2]); // throws IOException
out.writeBits(9, count - 20); // throws IOException
}
}
}
}
/**
* offLen のハフマン符号長のリストを書き出す
*
* @param offLenLen offLenFreq のハフマン符号長のリスト
* @exception IOException 入出力エラーが発生した場合
*/
private void writeOffLenLen(final int[] offLenLen) throws IOException {
int end = offLenLen.length;
while (0 < end && offLenLen[end - 1] == 0) {
end--;
}
int len = Bits.len(Bits.len(dictionarySize));
out.writeBits(len, end); // throws IOException
int index = 0;
while (index < end) {
len = offLenLen[index++];
if (len <= 6) {
out.writeBits(3, len); // throws IOException
} else {
out.writeBits(len - 3, (1 << len - 3) - 2);// throws IOException
}
}
}
/**
* 配列内の 0でない要素数を得る。
*
* @param array 配列
* @return 配列内の 0でない要素数
*/
private static int countNoZeroElement(final int[] array) {
int count = 0;
for (final int element : array) {
if (0 != element) {
count++;
}
}
return count;
}
/**
* 配列内の 0でない最初の要素を得る。
*
* @param array 配列
* @return 配列内の 0でない最初の要素 全ての要素が0の場合は 0を返す。
*/
private static int getNoZeroElementIndex(final 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(final int[] indexes, final int[][] arrays) {
if (1 < indexes.length) {
final int[] array = new int[arrays[0].length];
for (final int indexe : indexes) {
final int[] src = arrays[indexe];
for (int j = 0; j < src.length; j++) {
array[j] += src[j];
}
}
return array;
}
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(final int[] codeLen) {
final int[] codeLenFreq = new int[StaticHuffman.LIMIT_LEN + 3];
int end = codeLen.length;
while (0 < end && codeLen[end - 1] == 0) {
end--;
}
int index = 0;
while (index < end) {
final 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;
}
/**
* 指定された頻度情報でハフマン符号を 出力した場合のビット数を得る。
*
* @param DictionarySize LZSS辞書サイズ
* @param codeFreq コード部の頻度情報
* @param offLenFreq オフセット部の長さの頻度情報
* @return この頻度情報でハフマン符号を出力した場合のビット数を得る。
*/
private static int calcHuffmanCodeLength(final int DictionarySize, final int[] codeFreq, final int[] offLenFreq) {
// ------------------------------------------------------------------
// 初期化
int length = 0;
int[] codeLen, offLenLen;
try {
codeLen = StaticHuffman.FreqListToLenList(codeFreq);
StaticHuffman.LenListToCodeList(codeLen);
offLenLen = StaticHuffman.FreqListToLenList(offLenFreq);
} catch (final BadHuffmanTableException exception) { // 発生しない
throw new Error("caught the BadHuffmanTableException which should be never thrown.");
}
// ------------------------------------------------------------------
// code 部のハフマン頻度表の長さを算出する。
length += 16;
if (2 <= countNoZeroElement(codeFreq)) {
final int[] codeLenFreq = createCodeLenFreq(codeLen);
final int[] codeLenLen = StaticHuffman.FreqListToLenList(codeLenFreq);
if (2 <= countNoZeroElement(codeLenFreq)) {
length += calcCodeLenLen(codeLenLen);
} else {
length += 5;
length += 5;
}
length += calcCodeLen(codeLen, codeLenLen);
} else {
length += 10;
length += 18;
}
// ------------------------------------------------------------------
// offLen 部のハフマン頻度表の長さを算出する。
if (2 <= countNoZeroElement(offLenFreq)) {
length += calcOffLenLen(DictionarySize, offLenLen);
} else {
final 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(final 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) {
final 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(final int[] codeLen, final 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) {
final 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(final int DictionarySize, final 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) {
final int len = offLenLen[index++];
if (len <= 6) {
length += 3;
} else {
length += len - 3;
}
}
return length;
}
/**
* 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(final int BlockNum, final int DivideNum) {
final 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(final int BlockNum, final int DivideNum) {
int index = 0;
final int patternNum = calcPatternNum(BlockNum, DivideNum);
final int[][] pattern = new int[patternNum][];
for (int div = 0; div < Math.min(BlockNum, DivideNum + 1); div++) {
// 分割位置を保持する配列。
// 配列内の値は、例えば 0の場合は Block[0] と Block[1] の間で分割することを意味する。
final 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++) {
final int len = divPos[i] - start + 1;
final int num = BlockNum - len;
pattern[index][i] = (num + 1) * num / 2 + start;
start += len;
}
final 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(final int BlockNum, final int DivideNum) {
int patternNum = 0;
for (int div = 0; div <= DivideNum; div++) {
final 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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy