//============================================================================== // Base64DecoderInputStream.java //============================================================================== package tribble.io; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.lang.IllegalArgumentException; import java.lang.String; import java.text.ParseException; import tribble.util.Base64Decoder; import tribble.io.ReaderInputStream; /******************************************************************************* * Base-64 decoding input stream. * *

* Provides an I/O stream that decodes radix-64 characters into binary data. * Printable 8-bit bytes (octets) are read from the input stream which encode * data as base-64 (a.k.a. radix-64) ASCII characters and are converted into * binary data bytes. * *

* See {@link tribble.util.Base64Decoder} for more information about base-64 * decoding. * * *

*
Source code:
*
* http://david.tribble.com/src/java/tribble/io/Base64DecoderInputStream.java *
*
Documentation:
*
* http://david.tribble.com/docs/tribble/io/Base64DecoderInputStream.html *
*
* * * @version $Revision: 1.3 $ $Date: 2009/02/04 03:49:46 $ * @since 2005-04-11 * @author David R. Tribble (david@tribble.com) *

* Copyright ©2005-2009 by David R. Tribble, all rights reserved.
* Permission is granted to any person or entity except those designated by * by the United States Department of State as a terrorist, or terrorist * government or agency, to use and distribute this source code provided * that the original copyright notice remains present and unaltered. * * @see Base64EncoderInputStream * @see Base64DecoderOutputStream * @see tribble.util.Base64Encoder */ public class Base64DecoderInputStream extends java.io.FilterInputStream { static final String REV = "@(#)tribble/io/Base64DecoderInputStream.java $Revision: 1.3 $ $Date: 2009/02/04 03:49:46 $\n"; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Static methods /*************************************************************************** * Test driver. * Read one or more base-64 encoded input files and write them as binary * output form. * *

* Usage *

* * java tribble.io.Base64DecoderInputStream * [-o outfile] file... * * * @param args * Command line arguments. * * @throws IOException * Thrown if an I/O (write) error occurs. * * @since 1.1, 2005-04-01 */ public static void main(String[] args) throws IOException { String outFname = "-"; OutputStream out; int rc = 0; int i; // Parse command options for (i = 0; i < args.length && args[i].charAt(0) == '-'; i++) { if (args[i].equals("-o")) outFname = args[++i]; else if (args[i].equals("-")) break; else throw new IOException("Bad option: '" + args[i] + "'"); } // Check args if (i >= args.length) { // Show command usage System.out.println("Read base-64 input files and write them in " + "binary output form."); System.out.println(); System.out.println("usage: java " + Base64DecoderInputStream.class.getName() + " [-o outfile] file..."); System.exit(255); } // Set up the output stream if (outFname.equals("-")) out = new BufferedOutputStream(System.out); else out = new BufferedOutputStream(new FileOutputStream(outFname)); // Read one or more input files for ( ; i < args.length; i++) { String inFname = null; try { InputStream in; // Read a named input file inFname = args[i]; if (inFname.equals("-")) in = System.in; else in = new FileInputStream(inFname); // Read and convert the input file from base-64 System.err.println(inFname); decode(in, out); // Clean up out.flush(); if (!inFname.equals("-")) in.close(); } catch (IOException ex) { System.err.println("Can't convert: " + inFname); System.err.println(ex.getMessage()); rc = 1; } } // Clean up if (!outFname.equals("-")) out.close(); System.exit(rc); } /*************************************************************************** * Read and convert the contents of an input stream. * This method is equivalent to the call: * {@link #decode(InputStream, OutputStream, int) decode}(in, out, 0). * * @param in * An input stream containing base-64 encoded character data to be converted * into binary 8-bit octet data. * * @param out * An output stream to which is written the decoded binary 8-bit octet data * bytes. * * @throws IOException * Thrown if an I/O (read or write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if in or out is null. * * @see #decode(OutputStream) decode() * * @since 1.3, 2009-02-03 */ public static void decode(InputStream in, OutputStream out) throws IOException { // Read and convert base-64 characters from the input stream, writing // the converted 8-bit octets to the output stream decode(in, out, 0); } /*************************************************************************** * Read and convert the contents of an input stream. * *

* The entire contents of an input stream are read as base-64 encoded * characters and converted into 8-bit octets, which are then written to an * output stream. * *

* Note that the output stream is flushed after the converted data is * written, but is not closed, i.e., method flush() is called * but close() is not. * * @param in * An input stream containing base-64 encoded character data to be converted * into binary 8-bit octet data. * * @param out * An output stream to which is written the decoded binary 8-bit octet data * bytes. * * @param bufLen * Specifies the size, in bytes, of the internal buffer to use for reading * data from the input stream. Specifying a value less than 4 causes a * default size of 64K (65,536) to be used. * * @throws IOException * Thrown if an I/O (read or write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if in or out is null. * * @see #decode(OutputStream, int) decode() * * @since 1.3, 2009-02-03 */ public static void decode(InputStream in, OutputStream out, int bufLen) throws IOException { Base64DecoderInputStream dec; // Sanity check if (out == null) throw new NullPointerException(); // Read and convert base-64 characters from the input stream, writing // the converted 8-bit octets to the output stream dec = new Base64DecoderInputStream(in); dec.decode(out, bufLen); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Variables /** Pending input queue. */ protected byte[] m_inBuf = new byte[4*16]; /** Output queue. */ protected byte[] m_outBuf = new byte[3*16]; /** Pending input queue length. */ protected short m_inLen; /** Next output byte queue index. */ protected short m_outPos; /** Current output byte queue length. */ protected short m_outLen; /** End of input stream reached. */ protected boolean m_eof; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors /*************************************************************************** * Constructor. * * @param in * The underlying input stream for this input stream. * * @since 1.1, 2005-04-11 */ public Base64DecoderInputStream(InputStream in) { // Establish the underlying input stream super(in); } /*************************************************************************** * Constructor. * * @param in * The underlying input stream for this input stream. * * @since 1.1, 2005-04-11 */ public Base64DecoderInputStream(Reader in) { // Establish the underlying input stream super(new ReaderInputStream(in)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Methods /*************************************************************************** * Close this input stream. * *

* Any pending base-64 character input is discarded. * *

* Subsequent attempts to read from this input stream will cause exceptions * to be thrown. * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2005-04-11 */ public void close() throws IOException //overrides java.io.InputStream { if (this.in == null) return; // Clean up m_inBuf = null; m_outBuf = null; // Close and disassociate the underlying input stream this.in.close(); this.in = null; } /*************************************************************************** * Read a character from this input stream. * *

* A base-64 character encoding is converted into its corresponding 8-bit * binary code (octet) after being read from the underlying input stream. * * @return * An 8-bit character code, or -1 if the end of the input stream has been * reached. * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2005-04-11 */ public int read() throws IOException //overrides java.io.InputStream { // Ensure that there are converted bytes in the output queue if (m_outPos >= m_outLen) { // Read and convert the next base-64 input line readPending(); } // Extract the next byte from the converted output queue if (m_outPos < m_outLen) { int b; // Pop the next byte from the output queue b = m_outBuf[m_outPos++] & 0xFF; return b; } else { // No more input bytes to convert return -1; } } /*************************************************************************** * Reads up to len bytes of data from this input stream into an * array of bytes. * This method blocks until some input is available. * * @param buf * The buffer into which the data is read. * * @param off * The start offset within buf to read data into. * * @param len * The maximum number of bytes read into buf. * * @return * The total number of bytes read into the array, or -1 if there is no more * data because the end of the stream has been reached. * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2005-04-11 */ public int read(byte[] buf, int off, int len) throws IOException //overrides java.io.InputStream { int n; // Read multiple bytes from the input stream len += off; n = off; while (n < len) { byte b; // Ensure that there are converted bytes in the output queue if (m_outPos >= m_outLen) { // Read and convert the next base-64 input line readPending(); } // Extract the next byte from the converted output queue if (m_outPos < m_outLen) { // Get the next byte from the output queue b = m_outBuf[m_outPos++]; } else { // No more converted output bytes, end of the input stream if (n == off) return -1; break; } // Append the converted output byte to the read buffer buf[n++] = b; } // Return the number of bytes written to the read buffer return (n - off); } /*************************************************************************** * Retrieve the underlying input stream for this output stream. * *

* Note that this method is not specified by the standard * {@link java.io.FilterInputStream} library class, but is provided as a * convenient enhancement. * * @return * The underlying input stream for this stream, or null if it has been * closed. * * @since 1.1, 2005-04-11 */ public InputStream getInput() { // Get the input stream return this.in; } /*************************************************************************** * Read and convert the contents of an input stream. * This method is equivalent to the call: * {@link #decode(OutputStream, int) decode}(in, 0). * * @param out * An output stream to which is written the decoded binary 8-bit octet data * bytes. * * @throws IOException * Thrown if an I/O (read or write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if in is null. * * @see #decode(OutputStream, int) decode() * * @since 1.3, 2009-02-03 */ public void decode(OutputStream out) throws IOException { // Read and convert base-64 characters from the input stream decode(out, 0); } /*************************************************************************** * Read and convert the contents of an input stream. * *

* The entire contents of an input stream are read as base-64 encoded * characters and converted into 8-bit octets, which are then written to this * output stream. * *

* Note that the output stream is flushed after the converted data is * written, but is not closed, i.e., method flush() is called * but close() is not. * * @param out * An output stream to which is written the decoded binary 8-bit octet data * bytes. * * @param bufLen * Specifies the size, in bytes, of the internal buffer to use for reading * data from the input stream. Specifying a value less than 4 causes a * default size of 64K (65,536) to be used. * * @throws IOException * Thrown if an I/O (read or write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if in is null. * * @since 1.3, 2009-02-03 */ public void decode(OutputStream out, int bufLen) throws IOException { byte[] buf; // Sanity check if (out == null) throw new NullPointerException(); // Allocate an input buffer if (bufLen < 4) bufLen = 64*1024; buf = new byte[bufLen]; // Read and convert the base-64 input stream into octets for (;;) { int len; // Read and convert a block of base-64 input characters len = read(buf); if (len < 0) break; out.write(buf, 0, len); } // Clean up out.flush(); } /*************************************************************************** * Read base-64 encoded data bytes from the input stream and convert them * into data bytes (octets). * *

* Note that this method should only be called if {@link #m_outPos} is * greater than {@link #m_outLen}, indicating that the output queue has been * exhausted. * * @throws IOException * Thrown if an I/O (read) error occurs. * * @since 1.1, 2005-04-11 */ protected void readPending() throws IOException { int n; // Fill the input buffer with base-64 character bytes while (m_inLen < 4*16) { int ch; // Read a base-64 character from the input stream ch = this.in.read(); if (ch < 0) { // End of input stream reached m_eof = true; break; } // Filter out (ignore) noise characters if (ch > ' ' && ch <= 'z') m_inBuf[m_inLen++] = (byte) ch; } // Convert groups of four 6-bit sextets into three 8-bit octets m_outLen = 0; m_outPos = 0; if (m_eof) n = m_inLen; else n = m_inLen/4 * 4; if (n > 0) { try { // Convert base-64 sextets into 8-bit octets m_outLen = (short) Base64Decoder.decodeBytes(m_inBuf, 0, n, m_outBuf, 0); } catch (ParseException ex) { IOException ex2; // Base-64 conversion error, fail ex2 = new IOException(ex.getMessage()); //ex2.initCause(ex); // JRE 1.4+ throw (ex2); } // Save the leftover partial sextet group, if any m_inLen -= n; if (m_inLen > 0) { int i, j; for (i = 0, j = n; i < m_inLen; i++, j++) m_inBuf[i] = m_inBuf[j]; } } } } // End Base64DecoderInputStream.java