uk.ac.starlink.util.Base64InputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of stil Show documentation
Show all versions of stil Show documentation
Starlink Tables Infrastructure Library
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Network Security Services for Java.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package uk.ac.starlink.util;
import java.io.*;
import java.util.Arrays;
/**
* Reads in base-64 encoded input and spits out the raw binary decoding.
*
* @author Mozilla project
*/
public class Base64InputStream extends FilterInputStream {
private static final int WOULD_BLOCK = -2;
//
// decoding table
//
private static int[] table = new int[256];
// one-time initialization of decoding table
static {
int i;
for( i=0; i < 256; ++i) {
table[i] = -1;
}
int c;
for( c = 'A', i=0; c <= 'Z'; ++c, ++i) {
table[c] = i;
}
for( c = 'a'; c <= 'z'; ++c, ++i) {
table[c] = i;
}
for( c='0'; c <= '9'; ++c, ++i) {
table[c] = i;
}
table['+'] = 62;
table['/'] = 63;
}
// prev is the previous significant character read from the in stream.
// Significant characters are those that are part of the encoded data,
// as opposed to whitespace.
private int prev, savedPrev;
// state is the current state of our state machine. The states are 1-4,
// indicating which character of the current 4-character block we
// are looking for. After state 4 we wrap back to state 1. The state
// is not advanced when we read an insignificant character (such as
// whitespace).
private int state = 1, savedState;
public Base64InputStream(InputStream in) {
super(in);
}
public long skip(long n) throws IOException {
long count = 0;
while( (count < n) && (read() != -1) ) {
++count;
}
return count;
}
/**
* param block Whether or not to block waiting for input.
*/
private int read(boolean block) throws IOException {
int cur, ret=0;
boolean done = false;
while(!done) {
if( in.available() < 1 && !block) {
return WOULD_BLOCK;
}
cur = in.read();
switch(state) {
case 1:
if( cur == -1 ) {
// end of file
return -1;
}
if( cur == '=' ) {
throw new IOException("Invalid pad character");
}
if( table[cur] != -1 ) {
prev = cur;
state = 2;
}
break;
case 2:
if( cur == -1 ) {
throw new EOFException("Unexpected end-of-file");
}
if( cur == '=' ) {
throw new IOException("Invalid pad character");
}
if( table[cur] != -1 ) {
ret = (table[prev]<<2) | ((table[cur]&0x30)>>4);
prev = cur;
state = 3;
done = true;
}
break;
case 3:
if( cur == -1 ) {
throw new EOFException("Unexpected end-of-file");
}
if( cur == '=' ) {
// pad character
state = 4;
return -1;
}
if( table[cur] != -1 ) {
ret = ((table[prev]&0x0f)<<4) | ((table[cur]&0x3c)>>2);
prev = cur;
state = 4;
done = true;
}
break;
case 4:
if( cur == -1 ) {
throw new EOFException("Unexpected end-of-file");
}
if( cur == '=' ) {
// pad character
state = 1;
return -1;
}
if( table[cur] != -1 ) {
ret = ((table[prev]&0x03)<<6) | table[cur];
state = 1;
done = true;
}
break;
default:
assert false;
break;
}
}
return ret;
}
public int read() throws IOException {
return read(true);
}
public int read(byte[] b, int off, int len) throws IOException {
int count = 0;
if( len < 0 ) {
throw new IndexOutOfBoundsException("len is negative");
}
if( off < 0 ) {
throw new IndexOutOfBoundsException("off is negative");
}
while( count < len ) {
int cur = read(count==0);
if( cur == -1 ) {
// end-of-file
if( count == 0 ) {
return -1;
} else {
return count;
}
}
if( cur == WOULD_BLOCK ) {
assert count>0;
return count;
}
assert cur >= 0 && cur <= 255;
b[off+(count++)] = (byte) cur;
}
return count;
}
public int available() throws IOException {
// We really don't know how much is left. in.available() could all
// be whitespace.
return 0;
}
public boolean markSupported() {
return in.markSupported();
}
public void mark(int readlimit) {
in.mark(readlimit);
savedPrev = prev;
savedState = state;
}
public void close() throws IOException {
in.close();
}
public void reset() throws IOException {
in.reset();
prev = savedPrev;
state = savedState;
}
}