//============================================================================== // Base64EncoderInputStream.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.lang.IllegalArgumentException; import java.lang.String; import tribble.util.Base64Encoder; /******************************************************************************* * Base-64 encoding input stream. * *
* Provides an I/O stream that encodes binary data as base-64 characters. * 8-bit bytes (octets) are converted into printable 8-bit characters that encode * the data as base-64 (a.k.a. radix-64) ASCII characters as they are read from * the input stream. * *
* See {@link tribble.util.Base64Encoder} for more information about base-64 * encoding. * * *
* 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 Base64DecoderInputStream
* @see Base64EncoderOutputStream
* @see tribble.util.Base64Encoder
*/
public class Base64EncoderInputStream
extends java.io.FilterInputStream
{
static final String REV =
"@(#)tribble/io/Base64EncoderInputStream.java $Revision: 1.3 $ $Date: 2009/02/04 03:41:26 $\n";
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Constants
//----------------------------------
// Newline output styles
/** Newline output style: No newline sequences. */
public static final short NEWLINE_NONE = 0;
/** Newline output style: Single byte ('\n'). */
public static final short NEWLINE_BYTE = 1;
/** Newline output style: Native platform character sequence. */
public static final short NEWLINE_NATIVE = 2;
/** Platform-specific newline character sequence. */
protected static final byte[] NEWLINE =
System.getProperty("line.separator").getBytes();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Static methods
/***************************************************************************
* Test driver.
* Read one or more input files and write them in base-64 output form.
*
*
* Usage *
* * java tribble.io.Base64EncoderInputStream * [-o outfile] file... * * * @param args * Command line arguments. * * @throws IOException * Thrown if an I/O error occurs. * * @since 1.1, 2005-04-11 */ 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 binary input files and convert them to " + "base-64 character form."); System.out.println(); System.out.println("usage: java " + Base64EncoderInputStream.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 into base-64 System.err.println(inFname); encode(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 #encode(InputStream, OutputStream, int) encode}(in, out, 0). * * @param in * An input stream containing 8-bit octet data to be converted into base-64 * character output. * * @param out * An output stream to which the converted base-64 character output is to be * written. * * @throws IOException * Thrown if an I/O (read or write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if in or out is null. * * @see #encode(OutputStream) encode() * * @since 1.3, 2009-02-03 */ public static void encode(InputStream in, OutputStream out) throws IOException { // Read and convert the input stream into base-64 characters, writing // the encoded characters to the output stream encode(in, out, 0); } /*************************************************************************** * Read and convert the contents of an input stream. * *
* The entire contents of an input stream are read as 8-bit octets and * converted into base-64 encoded characters, 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 8-bit octet data to be converted into base-64 * character output. * * @param out * An output stream to which the converted base-64 character output is to be * written. * * @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 3 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 #encode(OutputStream, int) encode() * * @since 1.3, 2009-02-03 */ public static void encode(InputStream in, OutputStream out, int bufLen) throws IOException { Base64EncoderInputStream enc; // Sanity check if (in == null) throw new NullPointerException(); // Read and convert the input stream into base-64 characters, writing // the encoded characters to the output stream enc = new Base64EncoderInputStream(in); enc.encode(out, bufLen); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Variables /** Pending input queue. */ protected byte[] m_inBuf = new byte[3*16]; /** Output queue. */ protected byte[] m_outBuf = new byte[4*16 + NEWLINE.length]; /** 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; /** Newline output style. */ protected short m_newlineStyle = NEWLINE_NATIVE; /** 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 Base64EncoderInputStream(InputStream in) { // Establish the underlying input stream super(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. * *
* An 8-bit binary code (octet) is converted into its corresponding base-64 * character encoding after being read from the underlying input stream. * * @return * An ASCII base-64 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 input data group readPending(); } // Extract the next byte from the converted output queue if (m_outPos < m_outLen) { int b; // Pop the next character 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. * Each 8-bit binary code (octet) is converted into its corresponding base-64 * character encoding after being read from the underlying input stream. * * @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 input data 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 this input stream. * This method is equivalent to the call: * {@link #encode(OutputStream, int) encode}(out, 0). * * @param out * An output stream to which the converted base-64 character output is to be * written. * * @throws IOException * Thrown if an I/O (read or write) error occurs. * * @throws NullPointerException (unchecked) * Thrown if out is null. * * @see #encode(OutputStream, int) encode() * * @since 1.3, 2009-02-03 */ public void encode(OutputStream out) throws IOException { // Read and convert the input stream into base-64 characters, writing // the encoded characters to the output stream encode(out, 0); } /*************************************************************************** * Read and convert the contents of this input stream. * *
* The entire contents of this input stream are read as 8-bit octets and * converted into base-64 encoded characters, 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 out * An output stream to which the converted base-64 character output is to be * written. * * @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 3 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 out is null. * * @since 1.3, 2009-02-03 */ public void encode(OutputStream out, int bufLen) throws IOException { byte[] buf; // Sanity check if (out == null) throw new NullPointerException(); // Allocate an input buffer if (bufLen < 3) bufLen = 64*1024; buf = new byte[bufLen]; // Read and convert the input stream into base-64 characters for (;;) { int len; // Read and convert a block of input bytes len = read(buf); if (len < 0) break; // Write the converted base-64 characters to the output stream out.write(buf, 0, len); } // Clean up out.flush(); } /*************************************************************************** * Establish the handling of newline sequences for this output stream. * *
* This method provides a means for "prettying up" the character input of * this stream. * *
* The default style is {@link #NEWLINE_NATIVE}. * * @param style * Specifies the way that newlines are inserted into the input stream. * This value must be one of the {@link #NEWLINE_NONE NEWLINE_XXX} * constants: * *
* 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 8-bit data bytes (octets) while (m_inLen < m_inBuf.length) { int b; // Read a data byte from the input stream b = this.in.read(); if (b < 0) { // End of input stream reached m_eof = true; break; } // Append the data byte to the input queue m_inBuf[m_inLen++] = (byte) b; } // 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/3 * 3; if (n > 0) { // Convert groups of 8-bit octets into base-64 sextets m_outLen = (short) Base64Encoder.encodeAsBytes(m_inBuf, 0, n, m_outBuf, 0); // Add a terminating newline sequence switch (m_newlineStyle) { case NEWLINE_BYTE: // Append a single newline character m_outBuf[m_outLen++] = (byte) '\n'; break; case NEWLINE_NATIVE: // Append a native newline character sequence for (int i = 0; i < NEWLINE.length; i++) m_outBuf[m_outLen++] = NEWLINE[i]; break; case NEWLINE_NONE: default: // Do not append anything break; } // Save the leftover partial octet 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 Base64EncoderInputStream.java