From d62ea01062a0cdb6062c7b2e1d1fa848c0575ead Mon Sep 17 00:00:00 2001 From: Olyutorskii Date: Wed, 24 Aug 2011 01:06:07 +0900 Subject: [PATCH] =?utf8?q?binio=E3=83=91=E3=83=83=E3=82=B1=E3=83=BC?= =?utf8?q?=E3=82=B8=E5=B0=8E=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../sourceforge/mikutoga/binio/BinaryExporter.java | 352 +++++++++++++++++++++ .../mikutoga/binio/FeedableOutputStream.java | 53 ++++ .../jp/sourceforge/mikutoga/binio/FileUtils.java | 79 +++++ .../mikutoga/binio/IllegalTextExportException.java | 75 +++++ .../sourceforge/mikutoga/binio/TextExporter.java | 278 ++++++++++++++++ .../sourceforge/mikutoga/binio/package-info.java | 14 + 6 files changed, 851 insertions(+) create mode 100644 src/main/java/jp/sourceforge/mikutoga/binio/BinaryExporter.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/binio/FeedableOutputStream.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/binio/FileUtils.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/binio/IllegalTextExportException.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/binio/TextExporter.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/binio/package-info.java diff --git a/src/main/java/jp/sourceforge/mikutoga/binio/BinaryExporter.java b/src/main/java/jp/sourceforge/mikutoga/binio/BinaryExporter.java new file mode 100644 index 0000000..e3226b9 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/binio/BinaryExporter.java @@ -0,0 +1,352 @@ +/* + * binary data exporter + * + * License : The MIT License + * Copyright(c) 2011 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.binio; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.text.MessageFormat; + +/** + * バイナリデータの出力を行う汎用エクスポーター。 + *

デフォルトではリトルエンディアン形式で出力される。 + */ +public class BinaryExporter { + + private static final Charset CS_UTF16LE = Charset.forName("UTF-16LE"); + private static final Charset CS_WIN31J = Charset.forName("windows-31j"); + + private static final String ERRMSG_ILLENC = "illegal encoding"; + private static final String ERRMSG_TOOLONGTX = + "too long text: " + + "text \"{0}\" needs {1}bytes encoded but limit={2}bytes"; + + private static final int MASK_16 = 0xffff; + + private static final int BYTES_SHORT = Short .SIZE / Byte.SIZE; + private static final int BYTES_INT = Integer .SIZE / Byte.SIZE; + private static final int BYTES_LONG = Long .SIZE / Byte.SIZE; + private static final int BYTES_FLOAT = Float .SIZE / Byte.SIZE; + private static final int BYTES_DOUBLE = Double .SIZE / Byte.SIZE; + + private static final int BUFSZ_PRIM = BYTES_DOUBLE; + + + private final OutputStream ostream; + + private final byte[] barray; + private final ByteBuffer primbuf; + + private final TextExporter texporter_w31j; + private final TextExporter texporter_u16le; + private final FeedableOutputStream xos; + + + /** + * コンストラクタ。 + * @param ostream 出力ストリーム + * @throws NullPointerException 引数がnull + */ + public BinaryExporter(OutputStream ostream) throws NullPointerException{ + super(); + + if(ostream == null) throw new NullPointerException(); + this.ostream = ostream; + + this.barray = new byte[BUFSZ_PRIM]; + this.primbuf = ByteBuffer.wrap(this.barray); + this.primbuf.order(ByteOrder.LITTLE_ENDIAN); + + this.primbuf.clear(); + + this.texporter_w31j = new TextExporter(CS_WIN31J); + this.texporter_u16le = new TextExporter(CS_UTF16LE); + this.xos = new FeedableOutputStream(); + + return; + } + + + /** + * バイトオーダーを設定する。 + * @param order バイトオーダー + */ + public void setOrder(ByteOrder order){ + this.primbuf.order(order); + return; + } + + /** + * 設定されたバイトオーダーを返す。 + * @return 設定されたバイトオーダー + */ + public ByteOrder getOrder(){ + return this.primbuf.order(); + } + + /** + * 出力ストリームを閉じる。 + * @throws IOException 出力エラー + */ + public void close() throws IOException{ + this.ostream.close(); + return; + } + + /** + * 出力をフラッシュする。 + * I/O効率とデバッグ効率のバランスを考え、ご利用は計画的に。 + * @return this + * @throws IOException 出力エラー + */ + public BinaryExporter flush() throws IOException{ + this.ostream.flush(); + return this; + } + + /** + * byte値を出力する。 + * @param bVal byte値 + * @return this自身 + * @throws IOException 出力エラー + */ + public BinaryExporter dumpByte(byte bVal) throws IOException{ + this.ostream.write((int)bVal); + return this; + } + + /** + * byte値を出力する。 + * @param iVal int値。上位24bitは捨てられる。 + * @return this自身 + * @throws IOException 出力エラー + */ + public BinaryExporter dumpByte(int iVal) throws IOException{ + this.ostream.write(iVal); + return this; + } + + /** + * byte型配列を出力する。 + * @param array 配列 + * @return this自身 + * @throws IOException 出力エラー + */ + public BinaryExporter dumpByteArray(byte[] array) + throws IOException{ + dumpByteArray(array, 0, array.length); + return this; + } + + /** + * byte型配列の部分列を出力する。 + * @param array 配列 + * @param offset 出力開始位置 + * @param length 出力バイト数 + * @return this自身 + * @throws IOException 出力エラー + */ + public BinaryExporter dumpByteArray(byte[] array, int offset, int length) + throws IOException{ + this.ostream.write(array, offset, length); + return this; + } + + /** + * 内部バッファの先頭を出力する。 + * @param length 出力バイト数 + * @throws IOException 出力エラー + */ + private void dumpBuffer(int length) throws IOException{ + this.ostream.write(this.barray, 0, length); + return; + } + + /** + * short値を出力する。 + * @param sVal short値 + * @return this自身 + * @throws IOException 出力エラー + */ + @SuppressWarnings("PMD.AvoidUsingShortType") + public BinaryExporter dumpShort(short sVal) throws IOException{ + this.primbuf.putShort(0, sVal); + dumpBuffer(BYTES_SHORT); + return this; + } + + /** + * short値を出力する。 + * @param iVal int値。上位16bitは捨てられる。 + * @return this自身 + * @throws IOException 出力エラー + */ + @SuppressWarnings("PMD.AvoidUsingShortType") + public BinaryExporter dumpShort(int iVal) throws IOException{ + short sVal = (short)(iVal & MASK_16); + dumpShort(sVal); + return this; + } + + /** + * int値を出力する。 + * @param iVal int値 + * @return this自身 + * @throws IOException 出力エラー + */ + public BinaryExporter dumpInt(int iVal) throws IOException{ + this.primbuf.putInt(0, iVal); + dumpBuffer(BYTES_INT); + return this; + } + + /** + * long値を出力する。 + * @param lVal long値 + * @return this自身 + * @throws IOException 出力エラー + */ + public BinaryExporter dumpLong(long lVal) throws IOException{ + this.primbuf.putLong(0, lVal); + dumpBuffer(BYTES_LONG); + return this; + } + + /** + * float値を出力する。 + * @param fVal float値 + * @return this自身 + * @throws IOException 出力エラー + */ + public BinaryExporter dumpFloat(float fVal) throws IOException{ + this.primbuf.putFloat(0, fVal); + dumpBuffer(BYTES_FLOAT); + return this; + } + + /** + * double値を出力する。 + * @param dVal double値 + * @return this自身 + * @throws IOException 出力エラー + */ + public BinaryExporter dumpDouble(double dVal) throws IOException{ + this.primbuf.putDouble(0, dVal); + dumpBuffer(BYTES_DOUBLE); + return this; + } + + /** + * 詰め物パディングを出力する。 + * @param filler byte型配列によるパディングデータの並び。 + *

指定パディング長より長い部分は出力されない。 + * 指定パディング長に満たない場合は最後の要素が繰り返し出力される。 + *

配列長が0の場合は何も出力されない。 + * @param fillerLength パディング長。 + *

パディング長が0以下の場合は何も出力されない。 + * @return this + * @throws IOException 出力エラー + */ + public BinaryExporter dumpFiller(byte[] filler, int fillerLength) + throws IOException { + if(filler.length <= 0 || fillerLength <= 0){ + return this; + } + + byte lastData = filler[filler.length - 1]; + + int fillerIdx = 0; + for(int remain = fillerLength; remain > 0; remain--){ + byte bVal; + if(fillerIdx < filler.length) bVal = filler[fillerIdx++]; + else bVal = lastData; + dumpByte(bVal); + } + + return this; + } + + /** + * Windows31J文字列をを固定バイト長で出力する。 + * 固定バイト長に満たない箇所はパディングデータが詰められる。 + * @param text テキスト + * @param fixedLength 固定バイト長。0以下の場合は無制限。 + * @param filler 詰め物パディングデータ + * @return this + * @throws IOException 出力エラー + * @throws IllegalTextExportException テキスト出力エラー。 + * 出力が固定長を超えようとした、 + * もしくは不正なエンコードが行われたかのいずれか。 + */ + public BinaryExporter dumpFixedW31j(CharSequence text, + int fixedLength, + byte[] filler ) + throws IOException, IllegalTextExportException{ + this.xos.reset(); + + int encodedSize; + try{ + encodedSize = + this.texporter_w31j.encodeToByteStream(text, this.xos); + }catch(CharacterCodingException e){ + throw new IllegalTextExportException(ERRMSG_ILLENC, e); + } + + if( 0 < fixedLength && fixedLength < encodedSize ){ + String message = + MessageFormat.format(ERRMSG_TOOLONGTX, + text, encodedSize, fixedLength); + throw new IllegalTextExportException(message); + } + + int xferred = this.xos.feedStored(this.ostream); + + int remain = fixedLength - xferred; + if(remain > 0){ + dumpFiller(filler, remain); + } + + return this; + } + + /** + * UTF16-LE文字列をホレリス形式で出力する。 + * UTF16-LEエンコード結果のバイト長を + * 4byte整数としてリトルエンディアンで出力した後に、 + * エンコード結果のバイト列が出力される。 + * @param text 文字列 + * @return エンコードバイト列長 + * @throws IOException 出力エラー + * @throws IllegalTextExportException テキスト出力エラー。 + * 出力が固定長を超えようとした、 + * もしくは不正なエンコードが行われたかのいずれか。 + */ + public int dumpHollerithUtf16LE(CharSequence text) + throws IOException, IllegalTextExportException{ + this.xos.reset(); + + int encodedSize; + try{ + encodedSize = + this.texporter_u16le.encodeToByteStream(text, this.xos); + }catch(CharacterCodingException e){ + throw new IllegalTextExportException(ERRMSG_ILLENC, e); + } + + dumpInt(encodedSize); + + int xferred = this.xos.feedStored(this.ostream); + assert xferred == encodedSize; + + return xferred; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/binio/FeedableOutputStream.java b/src/main/java/jp/sourceforge/mikutoga/binio/FeedableOutputStream.java new file mode 100644 index 0000000..22bcc38 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/binio/FeedableOutputStream.java @@ -0,0 +1,53 @@ +/* + * output stream with feedable byte array + * + * License : The MIT License + * Copyright(c) 2011 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.binio; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * 蓄積された出力結果の他ストリームへの転送を可能とする + * {@link java.io.ByteArrayOutputStream}。 + */ +public class FeedableOutputStream extends ByteArrayOutputStream { + + /** + * コンストラクタ。 + * @see java.io.ByteArrayOutputStream#ByteArrayOutputStream() + */ + public FeedableOutputStream(){ + super(); + return; + } + + /** + * コンストラクタ。 + * @param size 初期バッファ長(byte単位)。 + * @see java.io.ByteArrayOutputStream#ByteArrayOutputStream(int) + */ + public FeedableOutputStream(int size){ + super(size); + return; + } + + /** + * 蓄積されたストリームデータを別ストリームへ転送する。 + *

何も蓄積されていなければ何も転送されない。 + *

蓄積されたストリームデータに変更は生じない。 + * @param os 別ストリーム + * @return 転送量。 + * @throws IOException 転送先の出力エラー + */ + public int feedStored(OutputStream os) throws IOException { + if(this.count <= 0) return 0; + os.write(this.buf, 0, this.count); + return this.count; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/binio/FileUtils.java b/src/main/java/jp/sourceforge/mikutoga/binio/FileUtils.java new file mode 100644 index 0000000..a84943e --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/binio/FileUtils.java @@ -0,0 +1,79 @@ +/* + * file utils + * + * License : The MIT License + * Copyright(c) 2011 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.binio; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; + +/** + * ファイルユーティリティ。 + */ +public final class FileUtils { + + /** + * コンストラクタ。 + */ + private FileUtils(){ + super(); + assert false; + throw new AssertionError(); + } + + + /** + * 既に存在する通常ファイルか否か判定する。 + * @param file 判定対象 + * @return 既に存在する通常ファイルならtrue + */ + public static boolean isExistsNormalFile(File file){ + if( ! file.exists() ) return false; + if( ! file.isFile() ) return false; + return true; + } + + /** + * 既に存在する特殊ファイルか否か判定する。 + * @param file 判定対象 + * @return 既に存在する特殊ファイルならtrue + */ + public static boolean isExistsUnnormalFile(File file){ + if( ! file.exists() ) return false; + if( file.isFile() ) return false; + return true; + } + + /** + * ファイルサイズを0に切り詰める。 + *

既に存在する通常ファイルでないならなにもしない。 + * @param file ファイル + * @throws IOException 入出力エラー + */ + public static void trunc(File file) throws IOException{ + if( ! isExistsNormalFile(file) ) return; + if(file.length() <= 0L) return; + + FileOutputStream foStream = new FileOutputStream(file); + try{ + FileChannel channnel = foStream.getChannel(); + try{ + channnel.truncate(0L); + }finally{ + channnel.close(); + } + }finally{ + foStream.close(); + } + + assert file.length() <= 0L; + + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/binio/IllegalTextExportException.java b/src/main/java/jp/sourceforge/mikutoga/binio/IllegalTextExportException.java new file mode 100644 index 0000000..e657dae --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/binio/IllegalTextExportException.java @@ -0,0 +1,75 @@ +/* + * illegal text exporting exception + * + * License : The MIT License + * Copyright(c) 2011 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.binio; + +import java.nio.charset.CharacterCodingException; + +/** + * バイナリファイルへの不正なテキスト情報の出力が検出された場合の例外。 + * {@link java.nio.charset.CharacterCodingException}に由来する異常系を + * {@link java.io.IOException}から分離するために設計された。 + *

+ * 異常系の発生した理由としては + *

+ * など。 + */ +@SuppressWarnings("serial") +public class IllegalTextExportException extends Exception { + + /** + * コンストラクタ。 + */ + public IllegalTextExportException(){ + super(); + return; + } + + /** + * コンストラクタ。 + * @param message 詳細メッセージ + */ + public IllegalTextExportException(String message){ + super(message); + return; + } + + /** + * コンストラクタ。 + * @param cause 原因 + */ + public IllegalTextExportException(CharacterCodingException cause){ + super(cause); + return; + } + + /** + * コンストラクタ。 + * @param message 詳細メッセージ + * @param cause 原因 + */ + public IllegalTextExportException(String message, + CharacterCodingException cause){ + super(message, cause); + return; + } + + /** + * 原因となったエンコーディング例外を返す。 + * @return 原因となったエンコーディング例外。なければnull。 + */ + @Override + public CharacterCodingException getCause(){ + Throwable superCause = super.getCause(); + return (CharacterCodingException) superCause; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/binio/TextExporter.java b/src/main/java/jp/sourceforge/mikutoga/binio/TextExporter.java new file mode 100644 index 0000000..7d54c7f --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/binio/TextExporter.java @@ -0,0 +1,278 @@ +/* + * text data exporter + * + * License : The MIT License + * Copyright(c) 2011 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.binio; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +/** + * 任意のエンコーダによるテキストデータのバイナリ出力を行う。 + */ +public class TextExporter { + + /** デフォルトの入力バッファサイズ(単位:char)。 */ + public static final int DEFBUFSZ_CHAR = 128; + /** デフォルトの出力バッファサイズ(単位:byte)。 */ + public static final int DEFBUFSZ_BYTE = 128; + + + private final CharsetEncoder encoder; + private CharBuffer cbuf = CharBuffer.allocate(DEFBUFSZ_CHAR); + private byte[] barray = new byte[DEFBUFSZ_BYTE]; + private ByteBuffer bbuf = ByteBuffer.wrap(barray); + + private CharSequence textData; + private int textLength; + private int inPos; + + + /** + * コンストラクタ。 + * @param encoder エンコーダ + * @throws NullPointerException 引数がnull + */ + public TextExporter(CharsetEncoder encoder) throws NullPointerException{ + super(); + + if(encoder == null) throw new NullPointerException(); + this.encoder = encoder; + this.encoder.onMalformedInput(CodingErrorAction.REPORT); + this.encoder.onUnmappableCharacter(CodingErrorAction.REPORT); + + return; + } + + /** + * コンストラクタ。 + * @param cs 文字セット + */ + public TextExporter(Charset cs){ + this(cs.newEncoder()); + return; + } + + /** + * エンコーダを返す。 + * @return エンコーダ + */ + public CharsetEncoder getEncoder(){ + return this.encoder; + } + + /** + * 入力内部バッファサイズを設定する。 + * @param newSize バッファサイズ。(単位:char) + * @throws IllegalArgumentException サイズ指定が正で無かった。 + */ + public void setCharBufSize(int newSize) + throws IllegalArgumentException { + if(newSize <= 0) throw new IllegalArgumentException(); + this.cbuf = CharBuffer.allocate(newSize); + this.cbuf.clear(); + return; + } + + /** + * 出力内部バッファサイズを設定する。 + * @param newSize バッファサイズ。(単位:byte) + * @throws IllegalArgumentException サイズ指定が正で無かった。 + */ + public void setByteBufSize(int newSize) + throws IllegalArgumentException { + if(newSize <= 0) throw new IllegalArgumentException(); + this.barray = new byte[newSize]; + this.bbuf = ByteBuffer.wrap(barray); + this.bbuf.clear(); + return; + } + + /** + * 与えられた文字列をエンコードしてストリームに出力する。 + * @param os 出力ストリーム + * @param text 文字列 + * @return 出力バイト長 + * @throws IOException 出力エラー + * @throws CharacterCodingException エンコードエラー + */ + public int dumpText(OutputStream os, CharSequence text) + throws IOException, CharacterCodingException { + this.textData = text; + + int total = 0; + try{ + total = dumpTextImpl(os); + }finally{ + this.textData = null; + } + + return total; + } + + /** + * 文字列をエンコードしてストリームに出力する。 + * @param os 出力ストリーム + * @return 出力バイト長 + * @throws IOException 出力エラー + * @throws CharacterCodingException エンコードエラー + */ + private int dumpTextImpl(OutputStream os) + throws IOException, CharacterCodingException { + reset(); + + int total = 0; + + for(;;){ + loadCharBuffer(); + CoderResult result = encode(); + if(result.isUnderflow()){ + this.cbuf.clear(); + if(hasMoreInput()){ + continue; + } + total += sweepByteBuffer(os); + break; + }else if(result.isOverflow()){ + total += sweepByteBuffer(os); + this.cbuf.compact(); + continue; + }else if(result.isError()){ + result.throwException(); + } + } + + total += flush(os); + + return total; + } + + /** + * 各種内部状態をリセットする。 + */ + private void reset(){ + this.cbuf.clear(); + this.bbuf.clear(); + + this.encoder.reset(); + + this.textLength = this.textData.length(); + this.inPos = 0; + + return; + } + + /** + * 入力バッファにまだ入力していない文字があるか判定する。 + * @return 入力バッファにまだ入力していない文字があればtrue + */ + private boolean hasMoreInput(){ + if(this.inPos < this.textLength) return true; + return false; + } + + /** + * 入力バッファに文字を埋める。 + *

入力バッファが一杯になるか入力文字列がなくなるまでバッファが埋められる。 + */ + private void loadCharBuffer(){ + while(this.cbuf.hasRemaining() && hasMoreInput()){ + char ch = this.textData.charAt(this.inPos++); + this.cbuf.put(ch); + } + this.cbuf.flip(); + return; + } + + /** + * エンコードを行う。 + * @return エンコード結果 + */ + private CoderResult encode(){ + boolean endOfInput; + if(hasMoreInput()) endOfInput = false; + else endOfInput = true; + + CoderResult result; + result = this.encoder.encode(this.cbuf, this.bbuf, endOfInput); + return result; + } + + /** + * 出力バッファを吐き出す。 + * @param os 出力ストリーム + * @return 出力バイト長 + * @throws IOException 出力エラー + */ + private int sweepByteBuffer(OutputStream os) throws IOException{ + this.bbuf.flip(); + + int total = this.bbuf.remaining(); + os.write(this.barray, 0, total); + + this.bbuf.clear(); + + return total; + } + + /** + * エンコーダをフラッシュする。 + * @param os 出力ストリーム + * @return 出力バイト長 + * @throws IOException 出力エラー + * @throws CharacterCodingException エンコーディングエラー + */ + private int flush(OutputStream os) + throws IOException, CharacterCodingException { + int total = 0; + + for(;;){ + CoderResult result = this.encoder.flush(this.bbuf); + if(result.isError()) result.throwException(); + + total += sweepByteBuffer(os); + + if(result.isUnderflow()) break; + } + + return total; + } + + /** + * 与えられた文字列のエンコード結果を格納先バイトストリームへ格納する。 + *

エンコード結果は格納先ストリームに追記される。 + * @param text 文字列 + * @param bos 格納先ストリーム + * @return エンコードしたバイト数。 + * @throws CharacterCodingException エンコードエラー + */ + @SuppressWarnings("PMD.AvoidRethrowingException") + public int encodeToByteStream(CharSequence text, + ByteArrayOutputStream bos ) + throws CharacterCodingException { + int result = 0; + try{ + result = dumpText(bos, text); + }catch(CharacterCodingException e){ + throw e; + }catch(IOException e){ + // ありえない + assert false; + throw new AssertionError(e); + } + + return result; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/binio/package-info.java b/src/main/java/jp/sourceforge/mikutoga/binio/package-info.java new file mode 100644 index 0000000..b36080b --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/binio/package-info.java @@ -0,0 +1,14 @@ +/* + * binary data I/O + * + * License : The MIT License + * Copyright(c) 2011 MikuToga Partners + */ + +/** + * バイナリデータの入出力を補佐する一連のクラス。 + */ + +package jp.sourceforge.mikutoga.binio; + +/* EOF */ -- 2.11.0