replicant-frameworks_native/common/java/com/android/common/Base64OutputStream.java
Doug Zongker f13b12d4f0 add NO_CLOSE flag for use by Base64OutputStream
Change-Id: Ib2884e7b3853e4e4b2e329edf47c6f64c2f165a7
2010-02-11 14:07:17 -08:00

154 lines
4.9 KiB
Java

/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.common;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* An OutputStream that does either Base64 encoding or decoding on the
* data written to it, writing the resulting data to another
* OutputStream.
*/
public class Base64OutputStream extends FilterOutputStream {
private final boolean encode;
private final Base64.EncoderState estate;
private final Base64.DecoderState dstate;
private final int flags;
private byte[] buffer = null;
private int bpos = 0;
private static byte[] EMPTY = new byte[0];
/**
* Performs Base64 encoding on the data written to the stream,
* writing the encoded data to another OutputStream.
*
* @param out the OutputStream to write the encoded data to
* @param flags bit flags for controlling the encoder; see the
* constants in {@link Base64}
*/
public Base64OutputStream(OutputStream out, int flags) {
this(out, flags, true);
}
/**
* Performs Base64 encoding or decoding on the data written to the
* stream, writing the encoded/decoded data to another
* OutputStream.
*
* @param out the OutputStream to write the encoded data to
* @param flags bit flags for controlling the encoder; see the
* constants in {@link Base64}
* @param encode true to encode, false to decode
*/
public Base64OutputStream(OutputStream out, int flags, boolean encode) {
super(out);
this.flags = flags;
this.encode = encode;
if (encode) {
estate = new Base64.EncoderState(flags, null);
dstate = null;
} else {
estate = null;
dstate = new Base64.DecoderState(flags, null);
}
}
public void write(int b) throws IOException {
// To avoid invoking the encoder/decoder routines for single
// bytes, we buffer up calls to write(int) in an internal
// byte array to transform them into writes of decently-sized
// arrays.
if (buffer == null) {
buffer = new byte[1024];
}
if (bpos >= buffer.length) {
// internal buffer full; write it out.
internalWrite(buffer, 0, bpos, false);
bpos = 0;
}
buffer[bpos++] = (byte) b;
}
/**
* Flush any buffered data from calls to write(int). Needed
* before doing a write(byte[], int, int) or a close().
*/
private void flushBuffer() throws IOException {
if (bpos > 0) {
internalWrite(buffer, 0, bpos, false);
bpos = 0;
}
}
public void write(byte[] b, int off, int len) throws IOException {
if (len <= 0) return;
flushBuffer();
internalWrite(b, off, len, false);
}
public void close() throws IOException {
flushBuffer();
internalWrite(EMPTY, 0, 0, true);
if ((flags & Base64.NO_CLOSE) == 0) {
out.close();
} else {
out.flush();
}
}
/**
* Write the given bytes to the encoder/decoder.
*
* @param finish true if this is the last batch of input, to cause
* encoder/decoder state to be finalized.
*/
private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException {
if (encode) {
// len*8/5+10 is an overestimate of the most bytes the
// encoder can produce for len bytes of input.
estate.output = embiggen(estate.output, len*8/5+10);
Base64.encodeInternal(b, off, len, estate, finish);
out.write(estate.output, 0, estate.op);
} else {
// len*3/4+10 is an overestimate of the most bytes the
// decoder can produce for len bytes of input.
dstate.output = embiggen(dstate.output, len*3/4+10);
if (!Base64.decodeInternal(b, off, len, dstate, finish)) {
throw new IOException("bad base-64");
}
out.write(dstate.output, 0, dstate.op);
}
}
/**
* If b.length is at least len, return b. Otherwise return a new
* byte array of length len.
*/
private byte[] embiggen(byte[] b, int len) {
if (b == null || b.length < len) {
return new byte[len];
} else {
return b;
}
}
}