4 * License : The MIT License
5 * Copyright(c) 2011 MikuToga Partners
8 package jp.sourceforge.mikutoga.binio;
10 import java.io.ByteArrayOutputStream;
11 import java.io.Closeable;
12 import java.io.Flushable;
13 import java.io.IOException;
14 import java.io.OutputStream;
15 import java.nio.charset.CharacterCodingException;
16 import java.nio.charset.Charset;
17 import java.text.MessageFormat;
20 * バイナリデータの出力を行う汎用エクスポーター。
21 * <p>デフォルトではリトルエンディアン形式で出力される。
23 public class BinaryExporter implements Closeable, Flushable{
25 private static final Charset CS_UTF16LE = Charset.forName("UTF-16LE");
26 private static final Charset CS_WIN31J = Charset.forName("windows-31j");
28 private static final String ERRMSG_ILLENC = "illegal encoding";
29 private static final String ERRMSG_TOOLONGTX =
31 + "text \"{0}\" needs {1}bytes encoded but limit={2}bytes";
33 private static final int MASK_16 = 0xffff;
34 private static final int MASK_8 = 0xff;
36 private static final int BYTES_SHORT = Short .SIZE / Byte.SIZE;
37 private static final int BYTES_INT = Integer .SIZE / Byte.SIZE;
38 private static final int BYTES_LONG = Long .SIZE / Byte.SIZE;
39 private static final int BYTES_FLOAT = Float .SIZE / Byte.SIZE;
40 private static final int BYTES_DOUBLE = Double .SIZE / Byte.SIZE;
42 private static final int BUFSZ_PRIM = BYTES_DOUBLE;
45 private final OutputStream ostream;
47 private final byte[] barray;
49 private final TextExporter texporter_w31j;
50 private final TextExporter texporter_u16le;
51 private final ByteArrayOutputStream xos;
56 * @param ostream 出力ストリーム
57 * @throws NullPointerException 引数がnull
59 public BinaryExporter(OutputStream ostream) throws NullPointerException{
62 if(ostream == null) throw new NullPointerException();
63 this.ostream = ostream;
65 this.barray = new byte[BUFSZ_PRIM];
67 this.texporter_w31j = new TextExporter(CS_WIN31J);
68 this.texporter_u16le = new TextExporter(CS_UTF16LE);
69 this.xos = new ByteArrayOutputStream();
77 * @throws IOException 出力エラー
80 public void close() throws IOException{
87 * I/O効率とデバッグ効率のバランスを考え、ご利用は計画的に。
88 * @throws IOException 出力エラー
91 public void flush() throws IOException{
100 * @throws IOException 出力エラー
102 public BinaryExporter dumpByte(byte bVal) throws IOException{
103 this.ostream.write((int)bVal);
109 * @param iVal int値。上位24bitは捨てられる。
111 * @throws IOException 出力エラー
113 public BinaryExporter dumpByte(int iVal) throws IOException{
114 this.ostream.write(iVal);
122 * @throws IOException 出力エラー
124 public BinaryExporter dumpByteArray(byte[] array)
126 dumpByteArray(array, 0, array.length);
133 * @param offset 出力開始位置
134 * @param length 出力バイト数
136 * @throws IOException 出力エラー
138 public BinaryExporter dumpByteArray(byte[] array, int offset, int length)
140 this.ostream.write(array, offset, length);
145 * short値をリトルエンディアンで出力する。
148 * @throws IOException 出力エラー
150 @SuppressWarnings("PMD.AvoidUsingShortType")
151 public BinaryExporter dumpLeShort(short sVal) throws IOException{
152 this.barray[0] = (byte)( (sVal >> 0) & MASK_8 );
153 this.barray[1] = (byte)( (sVal >> 8) & MASK_8 );
155 this.ostream.write(this.barray, 0, BYTES_SHORT);
161 * short値をリトルエンディアンで出力する。
162 * @param iVal int値。上位16bitは捨てられる。
164 * @throws IOException 出力エラー
166 @SuppressWarnings("PMD.AvoidUsingShortType")
167 public BinaryExporter dumpLeShort(int iVal) throws IOException{
168 short sVal = (short)(iVal & MASK_16);
174 * int値をリトルエンディアンで出力する。
177 * @throws IOException 出力エラー
179 public BinaryExporter dumpLeInt(int iVal) throws IOException{
180 this.barray[0] = (byte)( (iVal >> 0) & MASK_8 );
181 this.barray[1] = (byte)( (iVal >> 8) & MASK_8 );
182 this.barray[2] = (byte)( (iVal >> 16) & MASK_8 );
183 this.barray[3] = (byte)( (iVal >> 24) & MASK_8 );
185 this.ostream.write(this.barray, 0, BYTES_INT);
191 * long値をリトルエンディアンで出力する。
194 * @throws IOException 出力エラー
196 public BinaryExporter dumpLeLong(long lVal) throws IOException{
197 this.barray[0] = (byte)( (lVal >> 0) & 0xffL );
198 this.barray[1] = (byte)( (lVal >> 8) & 0xffL );
199 this.barray[2] = (byte)( (lVal >> 16) & 0xffL );
200 this.barray[3] = (byte)( (lVal >> 24) & 0xffL );
201 this.barray[4] = (byte)( (lVal >> 32) & 0xffL );
202 this.barray[5] = (byte)( (lVal >> 40) & 0xffL );
203 this.barray[6] = (byte)( (lVal >> 48) & 0xffL );
204 this.barray[7] = (byte)( (lVal >> 56) & 0xffL );
206 this.ostream.write(this.barray, 0, BYTES_LONG);
212 * float値をリトルエンディアンで出力する。
215 * @throws IOException 出力エラー
217 public BinaryExporter dumpLeFloat(float fVal) throws IOException{
218 int rawiVal = Float.floatToRawIntBits(fVal);
220 this.barray[0] = (byte)( (rawiVal >> 0) & MASK_8 );
221 this.barray[1] = (byte)( (rawiVal >> 8) & MASK_8 );
222 this.barray[2] = (byte)( (rawiVal >> 16) & MASK_8 );
223 this.barray[3] = (byte)( (rawiVal >> 24) & MASK_8 );
225 this.ostream.write(this.barray, 0, BYTES_FLOAT);
231 * double値をリトルエンディアンで出力する。
232 * @param dVal double値
234 * @throws IOException 出力エラー
236 public BinaryExporter dumpLeDouble(double dVal) throws IOException{
237 long rawlVal = Double.doubleToRawLongBits(dVal);
239 this.barray[0] = (byte)( (rawlVal >> 0) & MASK_8 );
240 this.barray[1] = (byte)( (rawlVal >> 8) & MASK_8 );
241 this.barray[2] = (byte)( (rawlVal >> 16) & MASK_8 );
242 this.barray[3] = (byte)( (rawlVal >> 24) & MASK_8 );
243 this.barray[4] = (byte)( (rawlVal >> 32) & MASK_8 );
244 this.barray[5] = (byte)( (rawlVal >> 40) & MASK_8 );
245 this.barray[6] = (byte)( (rawlVal >> 48) & MASK_8 );
246 this.barray[7] = (byte)( (rawlVal >> 56) & MASK_8 );
248 this.ostream.write(this.barray, 0, BYTES_DOUBLE);
255 * @param filler byte型配列によるパディングデータの並び。
256 * <p>指定パディング長より長い部分は出力されない。
257 * 指定パディング長に満たない場合は最後の要素が繰り返し出力される。
258 * <p>配列長が0の場合は何も出力されない。
259 * @param fillerLength パディング長。
260 * <p>パディング長が0以下の場合は何も出力されない。
262 * @throws IOException 出力エラー
264 public BinaryExporter dumpFiller(byte[] filler, int fillerLength)
266 if(filler.length <= 0 || fillerLength <= 0){
270 byte lastData = filler[filler.length - 1];
273 for(int remain = fillerLength; remain > 0; remain--){
275 if(fillerIdx < filler.length) bVal = filler[fillerIdx++];
276 else bVal = lastData;
284 * Windows31J文字列をを固定バイト長で出力する。
285 * 固定バイト長に満たない箇所はパディングデータが詰められる。
287 * @param fixedLength 固定バイト長。0以下の場合は無制限。
288 * @param filler 詰め物パディングデータ
290 * @throws IOException 出力エラー
291 * @throws IllegalTextExportException テキスト出力エラー。
293 * もしくは不正なエンコードが行われたかのいずれか。
295 public BinaryExporter dumpFixedW31j(CharSequence text,
298 throws IOException, IllegalTextExportException{
304 this.texporter_w31j.encodeToByteStream(text, this.xos);
305 }catch(CharacterCodingException e){
306 throw new IllegalTextExportException(ERRMSG_ILLENC, e);
309 if( 0 < fixedLength && fixedLength < encodedSize ){
311 MessageFormat.format(ERRMSG_TOOLONGTX,
312 text, encodedSize, fixedLength);
313 throw new IllegalTextExportException(message);
316 this.xos.writeTo(this.ostream);
317 int xferred = this.xos.size();
319 int remain = fixedLength - xferred;
321 dumpFiller(filler, remain);
328 * UTF16-LE文字列をホレリス形式で出力する。
329 * UTF16-LEエンコード結果のバイト長を
330 * 4byte整数としてリトルエンディアンで出力した後に、
331 * エンコード結果のバイト列が出力される。
334 * @throws IOException 出力エラー
335 * @throws IllegalTextExportException テキスト出力エラー。
337 * もしくは不正なエンコードが行われたかのいずれか。
339 public int dumpHollerithUtf16LE(CharSequence text)
340 throws IOException, IllegalTextExportException{
346 this.texporter_u16le.encodeToByteStream(text, this.xos);
347 }catch(CharacterCodingException e){
348 throw new IllegalTextExportException(ERRMSG_ILLENC, e);
351 dumpLeInt(encodedSize);
353 this.xos.writeTo(this.ostream);
354 int xferred = this.xos.size();
355 assert xferred == encodedSize;