All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.continual.util.data.base64.Base64InputStream Maven / Gradle / Ivy

There is a newer version: 0.3.14
Show newest version
/*
 *	Copyright 2019, Continual.io
 *
 *	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 io.continual.util.data.base64;

import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;

public class Base64InputStream extends InputStream
{
	public Base64InputStream(InputStream upstream )
	{
		fUpstream = upstream;
		fPendingOutput = new LinkedList ();
		fEndOfStream = false;
	}

	private final LinkedList fPendingOutput;

	@Override
	public int available () throws IOException
	{
		fillBuffer ();
		return fPendingOutput.size ();
	}

	@Override
	public int read ()
		throws IOException
	{
		int result = -1;

		fillBuffer ();
		if ( fPendingOutput.size () > 0 )
		{
			result = fPendingOutput.remove ();
			if ( result < 0 )
			{
				result += 256;
			}
		}
		else if ( !fEndOfStream )
		{
			result = 0;
		}
		return result;
	}

	private byte[] decode ( char[] in )
	{
		int iLen = in.length;
		if ( iLen % 4 != 0 )
			throw new IllegalArgumentException (
				"Length of Base64 encoded input string is not a multiple of 4." );
		while ( iLen > 0 && in[iLen - 1] == '=' )
			iLen--;
		int oLen = ( iLen * 3 ) / 4;
		byte[] out = new byte [oLen];
		int ip = 0;
		int op = 0;
		while ( ip < iLen )
		{
			int i0 = in[ip++];
			int i1 = in[ip++];
			int i2 = ip < iLen ? in[ip++] : 'A';
			int i3 = ip < iLen ? in[ip++] : 'A';
			if ( i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127 )
				throw new IllegalArgumentException (
					"Illegal character in Base64 encoded data." );
			int b0 = Base64Constants.b64ToNibbles[i0];
			int b1 = Base64Constants.b64ToNibbles[i1];
			int b2 = Base64Constants.b64ToNibbles[i2];
			int b3 = Base64Constants.b64ToNibbles[i3];
			if ( b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0 )
				throw new IllegalArgumentException (
					"Illegal character in Base64 encoded data." );
			int o0 = ( b0 << 2 ) | ( b1 >>> 4 );
			int o1 = ( ( b1 & 0xf ) << 4 ) | ( b2 >>> 2 );
			int o2 = ( ( b2 & 3 ) << 6 ) | b3;
			out[op++] = (byte) o0;
			if ( op < oLen )
				out[op++] = (byte) o1;
			if ( op < oLen )
				out[op++] = (byte) o2;
		}
		return out;
	}

	private final InputStream fUpstream;
	private boolean fEndOfStream;

	private void fillBuffer () throws IOException
	{
		if ( fPendingOutput.size () == 0 )
		{
			// skip any newlines
			int first = fUpstream.read ();
			while ( first == Base64Constants.kNewline )
			{
				first = fUpstream.read ();
			}

			if ( first == -1 )
			{
				fEndOfStream = true;
			}
			else
			{
				// read 3 more bytes
				byte[] next3 = new byte[3];
				int readFromNext3 = fUpstream.read ( next3 );
				if ( readFromNext3 != 3 )
				{
					throw new IOException ( "Base64InputStream expects 4 bytes from its underlying stream but got " +
						(1+readFromNext3) );
				}
	
				char[] block = new char[4];
				block[0] = (char)((byte) first);
				for ( int i=0; i<3; i++ ) { block[i+1]=(char)next3[i]; }
	
				byte[] decoded = decode ( block );
				for ( byte b : decoded )
				{
					fPendingOutput.add ( b );
				}
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy