com.ibm.commons.util.io.base64.Base64InputStream Maven / Gradle / Ivy
/*
* © Copyright IBM Corp. 2012-2013
*
* 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.ibm.commons.util.io.base64;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* A Base64 content transfer encoding filter stream.
*
* From RFC 2045, section 6.8:
*
* The Base64 Content-Transfer-Encoding is designed to represent
* arbitrary sequences of octets in a form that need not be humanly
* readable. The encoding and decoding algorithms are simple, but the
* encoded data are consistently only about 33 percent larger than the
* unencoded data.
* @ibm-api
*/
public class Base64InputStream extends FilterInputStream {
private byte[] _buffer;
private int _buflen;
private int _index;
private byte[] _decodeBuf;
/**
* Constructs an input stream that decodes an underlying Base64-encoded
* stream.
* @param in the Base64-encoded stream
* @ibm-api
*/
public Base64InputStream(InputStream in) {
super(in);
_buflen = 0;
_index = 0;
_decodeBuf = new byte[4];
_buffer = new byte[3];
}
/**
* Reads the next byte of data from the input stream.
* @throws IOException IO Exception occurred
* @return next byte in data stream
*/
public int read() throws IOException {
if (_index >= _buflen) {
decode();
if (_buflen == 0) {
return -1;
}
_index = 0;
}
return _buffer[_index++] & 0xFF;
}
/**
* Reads up to len bytes of data from the input stream into an array of
* bytes.
* @param b buffer to put data
* @param off offset to start of buffer
* @param len number of bytes from buffer
* @throws IOException IO Exception occurred
* @return number of bytes read
*/
public int read(byte[] b, int off, int len) throws IOException {
try {
int l = 0;
for (; l < len; l++) {
int ch = read();
if (ch == -1) {
if (l == 0) {
return -1;
}
else {
break;
}
}
b[off+l] = (byte)ch;
}
return l;
} catch (IOException ioe) {
return -1;
}
}
/**
* Returns the number of bytes that can be read (or skipped over) from this
* input stream without blocking by the next caller of a method for this
* input stream.
* @throws IOException IO Exception occurred
* @return number of bytes that can be read
*/
public int available() throws IOException {
return(in.available() * 3) / 4 + (_buflen-_index);
}
/**
* Decode Base64 encoded buffer
* @throws IOException IO Exception occurred
*/
private void decode() throws IOException {
_buflen = 0;
// Loop until we hit EOF or non-line-termination char
int ch = Ascii.LF;
while (ch == Ascii.LF || ch == Ascii.CR){
ch = in.read();
if (ch == -1) {
return;
}
}
_decodeBuf[0] = (byte)ch;
int j = 3, l;
for (int k = 1; (l = in.read(_decodeBuf, k, j)) != j; k += l) {
if (l == -1) {
throw new IOException("Base64 encoding error"); // $NLS-Base64InputStream.Base64encodingerror-1$
}
j -= l;
}
byte b0 = IoConstants.B64_DST_MAP[_decodeBuf[0] & 0xFF];
byte b2 = IoConstants.B64_DST_MAP[_decodeBuf[1] & 0xFF];
_buffer[_buflen++] = (byte)(b0<<2 & 0xfc | b2 >>> 4 & 0x3);
if (_decodeBuf[2] != Ascii.EQUALS) {
b0 = b2;
b2 = IoConstants.B64_DST_MAP[_decodeBuf[2] & 0xFF];
_buffer[_buflen++] = (byte)(b0 << 4 & 0xf0 | b2 >>> 2 & 0xf);
if (_decodeBuf[3] != Ascii.EQUALS) {
byte b1 = b2;
b2 = IoConstants.B64_DST_MAP[_decodeBuf[3] & 0xFF];
_buffer[_buflen++] = (byte)(b1 << 6 & 0xc0 | b2 & 0x3f);
}
}
}
}