-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Thursday 29 November 2001 09:46 am, Richard Emberson wrote:
>  if sun.misc.BASE64Encoder is not in the classpath, then
> this code fails during class loading, not during execution.

I wrote my own Base64 encoder/decoder Input/OutputStreams for a personal 
project a while back.  They have not been tested thoroughly, and I don't have 
test cases at the moment, but they seem to work.  I've attached their source 
files.

The sources used to be GPL'd, but they were part of a project that never got 
released (an XML serialization/deserialization app call "Xerial", similar to 
JSX), so considering that I wrote them there shouldn't be any problems.  I 
removed the GPL license from the tops of the files.

Let met know if you are interested in using these as a replacement to Sun's 
Base64 encoder.  Feel free to change the packages to org.apache.something if 
you do.  I would love to be able to contribute something to this great 
project.

- -- 
Furthermore, I believe bacon prevents hair loss.
Peter Davis
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQE8BoTB/kafCzGPRfMRAgm7AJ9kxz2BIn+lyodkvvtP6ATy1q5lWwCeP0kE
69OAfEhRMPe2mEWFz7g6KJU=
=vFth
-----END PGP SIGNATURE-----
/**
 * Copyright 2001 Peter Davis.
 **/


package cx.pdavis.xerial;

import java.io.IOException;

/**
 * Thrown when <tt>Base64InputStream</tt> tries to  read input that is
 * not padded to a multiple of four ASCII sextets.
 *
 * @see Base64InputStream
 * @see   <a href="http://www.faqs.org/rfcs/rfc1521.html";>RFC    1521,
 * section 5.2"</a>
 **/
public class IllegalBase64PaddingException extends IOException {

    /**
     * Constructs  a new  IllegalBase64PaddingException with the given
     * detail message.
     * 
     * @param msg the detail message
     **/
    public IllegalBase64PaddingException(String msg) {
        super(msg);
    }
}
/**
 * Copyright 2001 Peter Davis.
 **/


package cx.pdavis.xerial;

import java.io.IOException;

/**
 * Thrown  when <tt>Base64InputStream</tt> tries  to read a quadruplet
 * that contains illegal characters.  Illegal characters are those not
 * in the ranges A-Z, a-z, 0-9, '+', or '/', an  '=' character that is
 * not in  the last  two  characters of  the   quadruplet, or an   '='
 * character not followed by another '=' character.
 *
 * @see Base64InputStream
 * @see   <a href="http://www.faqs.org/rfcs/rfc1521.html";>RFC    1521,
 * section 5.2"</a>
 **/
public class IllegalBase64QuadrupletException extends IOException {

    /**
     * Constructs  a  new    IllegalBase64QuadrupletException with the
     * given detail message.
     * 
     * @param msg the detail message
     **/
    public IllegalBase64QuadrupletException(String quadruplet) {
        super(quadruplet);
    }
}
/**
 * Copyright 2001 Peter Davis.
 **/

package cx.pdavis.xerial;

import java.io.OutputStream;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.IOException;

/**
 * Encodes binary bytes into an  ASCII compatible format, as specified
 * by    <a   href="http://www.faqs.org/rfcs/rfc1521.html";>RFC   1521,
 * section 5.2</a>.  This encoding is known as "Base64", because each 
 * character in   the encoded output  represents  one  of  64 possible
 * values (6  bits).  However, each character  still ocupies an entire
 * byte (8 bits), which means that encoded  data is consistantly about
 * 33% larger than unencoded data.
 *
 * <p>During  encoding, each set of  three  byte  octets (24 bits)  is
 * converted  into  four sextets.   Each  sextet is  then mapped to an
 * ASCII  value in the range of  A-Z, a-z, 0-9,   '+', or '/'.  If the
 * length  of the input is not   a multiple of  three  bytes, then the
 * output will  be padded with one or  two '='  characters so that the
 * length of the output is always a multiple of four sextets.
 *
 * <p>Users of this class should note  that, in order to maintain full
 * compliance with RFC 1521, [EMAIL PROTECTED]   #flush} should only be   
invoked
 * once per instance of <tt>Base64OutputStream</tt>.
 *
 * <p>Some implementations  of  Base64 encoders  use ASCII  characters
 * other than those in the  ranges given above in  an attempt to allow
 * encoded data to be compatible with the  range of characters allowed
 * in URLEncoded data.   The output of </tt>Base64OutputStream</tt> is
 * not compatible with those implementations.
 *
 * @see Base64InputStream
 * @see java.net.URLEncoder
 * @see   <a href="http://www.faqs.org/rfcs/rfc1521.html";>RFC    1521,
 * section 5.2"</a>
 **/
public class Base64OutputStream extends OutputStream {

    private final Writer out;
    private boolean closed;

    /**
     * Data   written    to this   <tt>Base64OutputStream</tt> will be
     * encoded and written to the given <tt>OutputStream</tt>.
     *
     * @param  os the OutputStream  to    which encoded data will   be
     * written
     **/
    public Base64OutputStream(OutputStream os) {
        this.out = new OutputStreamWriter(os);
        this.outBuffer = new char[outBufferLength = 1024];
    }

    /**
     * Data   written  to  this   <tt>Base64OutputStream</tt>  will be
     * encoded and written to the given <tt>Writer</tt>.
     *
     * @param w the Writer to which encoded data will be written
     **/ 
    public Base64OutputStream(Writer w) {
        this.out = w;
        this.outBuffer = new char[outBufferLength = 1024];
    }

    /**
     * Data   written    to this   <tt>Base64OutputStream</tt> will be
     * encoded and written to the given <tt>OutputStream</tt>.
     *
     * @param  os the OutputStream  to    which encoded data will   be
     * written
     * @param  bufferSize the size  of  the internal  buffer (in input
     * bytes, not encoded sextets)
     **/
    public Base64OutputStream(OutputStream os, int bufferSize) {
        this.out = new OutputStreamWriter(os);
        if(bufferSize == 0) {
            this.outBuffer = new char[outBufferLength = 4];
        } else if(bufferSize > 0) {
            bufferSize *= 1.25f;
            this.outBuffer = new char[outBufferLength = bufferSize + (4 - 
(bufferSize % 4))];
        } else {
            throw new IllegalArgumentException("bufferSize < 0");
        }            
    }

    /**
     * Data   written  to  this   <tt>Base64OutputStream</tt>  will be
     * encoded and written to the given <tt>Writer</tt>.
     *
     * @param w the Writer to which encoded data will be written
     * @param bufferSize the size   of the internal buffer  (in  input
     * bytes, not encoded sextets)
     **/ 
    public Base64OutputStream(Writer w, int bufferSize) {
        this.out = w;
        if(bufferSize == 0) {
            this.outBuffer = new char[outBufferLength = 4];
        } else if(bufferSize > 0) {
            bufferSize *= 1.25f;
            this.outBuffer = new char[outBufferLength = bufferSize + (4 - 
(bufferSize % 4))];
        } else {
            throw new IllegalArgumentException("bufferSize < 0");
        }
    }

    static final char[] alphabet = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
        'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
        'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
        'w', 'x', 'y', 'z', '0', '1', '2', '3',
        '4', '5', '6', '7', '8', '9', '+', '/',
    };

    private static final String lineSep = System.getProperty("line.separator", 
"\n");

    /**
     * Bytes are written in sets of 24  bits.  This field stores those
     * bits until the full 24 is ready to be written.
     **/
    private int buf; // = 0x00;
    /**
     * Counts the number of bytes stored in buf.   Always one of 0, 1,
     * or 2.  If there are 3 bytes stored in buf, they will be flushed
     * and mod will be set back to 0.
     **/
    private int mod; // = 0;

    /**
     * Stores all  written byte triplets/quadruplets until flushed for
     * efficiency.  Length must be a multiple of 4.
     **/
    private final char[] outBuffer;
    private final int outBufferLength;
    private int outBufferOffset;

    /**
     * According to RFC 1521, no more than 76 characters may be output
     * on a line.  This field stores the number of characters that has
     * been written since the beginning of output or the last newline.
     * colMod will never be more than colWrap.
     **/
    private int colMod; // = 0;

    /**
     * A  newline will be  output ever colWrap base64 characters (must
     * not be more than 76).  Default is 64.
     **/
    private static final int colWrap = 64;

    /**
     * Writes a single byte.
     *
     * @param b the byte to be written.   Only the low-order 8 bits of
     * the <tt>int</tt> value are used.
     *
     * @exception  IOException   if thrown   during   writing  to  the
     * underlying stream, or if this stream has been closed
     **/
    public synchronized void write(int b) throws IOException {
        if(closed) {
            throw new IOException("stream has been closed");
        }
        
        b &= 0xFF;
        switch(mod++) {
        case 0:
            buf = b << 16;
            break;
        case 1:
            buf |= b << 8;
            break;
        case 2:
            buf |= b;
            flush24(mod);
            break;
        default:
            throw new Error("impossible bytesWritten % 3 = "+(mod - 1));
        }
    }

    /**
     * Writes <tt>len</tt>  bytes retrieved from  the array <tt>b</tt>
     * starting from index <tt>offset</tt>.
     *
     * @param b the array containing the bytes to be written
     * @param  offset  no bytes in  <tt>b</tt>  before  this index are
     * written
     * @param len the number of bytes to write
     *
     * @exception  IOException   if thrown   during   writing  to  the
     * underlying stream, or if this stream has been closed
     **/
    public synchronized void write(byte[] b, int offset, int len) throws 
IOException {
        if(closed) {
            throw new IOException("stream has been closed");
        }

        // for efficiency, len and max are absolute indexes instead of
        // relative ones
        len += offset;

        // flush any previously written single bytes
        int flushed = 0;
        for(; mod != 0 && offset < len; flushed++) {
            write(b[offset++]);
        }

        // max is always less than len
        int max = (max = len - flushed) - (max % 3);

        // will remain zero unless single bytes are
        // written later
        mod = 0;

        // write multiples of three bytes.   This loop works just like
        // calling  write(int) multiple times, except  that mod is not
        // updated (since  we always  write  3 bytes)  and there is no
        // synchronization penalty.
        while(offset < max) {
            buf = (b[offset++] & 0xFF) << 16;
            buf |= (b[offset++] & 0xFF) << 8;
            buf |= b[offset++] & 0xFF;
            flush24(3);
        }

        // write single bytes for any leftovers
        while(offset < len) {
            write(b[offset++]);
        }
    }

    /**
     * Encodes the  the one, two, or three   bytes stored in  buf into
     * their four   corresponding  ASCII characters  and puts  them in
     * outBuffer. If  outBuffer  is   full,  it  is flushed   to   the
     * underlying stream.   If more than  colWrap characters have been
     * written, a line separator is also written.
     *
     * @param mod the number of bytes currently stored in buf
     **/
    private final void flush24(int mod) throws IOException {
        try {
            // REVISIT: should this be + 3?
            if(outBufferOffset + 4 >= outBufferLength) {
                flushOutBuffer();
            }
            
            char[] outBuffer = this.outBuffer;
            int buf = this.buf;

            // divide 24 bits  in buf  into 4  sextets.  If there  are
            // less than 24 bits (mod < 3), d and  c will be zero, but
            // this is handled later.
            int d = buf & 0x3F;
            buf >>>= 6;
            int c = buf & 0x3F;
            buf >>>= 6;
            int b = buf & 0x3F;
            buf >>>= 6;
            int a = buf & 0x3F;

            // lookup each 
            switch(mod) {
            case 3:
                outBuffer[outBufferOffset++] = alphabet[a];
                outBuffer[outBufferOffset++] = alphabet[b];
                outBuffer[outBufferOffset++] = alphabet[c];
                outBuffer[outBufferOffset++] = alphabet[d];
                break;
            case 2:
                outBuffer[outBufferOffset++] = alphabet[a];
                outBuffer[outBufferOffset++] = alphabet[b];
                outBuffer[outBufferOffset++] = alphabet[c];
                outBuffer[outBufferOffset++] = '=';
                break;
            case 1:
                outBuffer[outBufferOffset++] = alphabet[a];
                outBuffer[outBufferOffset++] = alphabet[b];
                outBuffer[outBufferOffset++] = '=';
                outBuffer[outBufferOffset++] = '=';
                break;
            case 0:
                // we  shouldn't be here at  all,   but might as  well
                // ignore it since there are no bytes to write
                return; // so that no newline is written;
                        // execute through finally as well
            default:
                throw new Error("impossible bytesWritten % 3 = "+(mod - 1));
            }

            if((colMod += 4) >= colWrap) {
                colMod = 0;

                int lineSepLength = lineSep.length();
                for(int i=0, il=lineSepLength; i<il; i++) {
                    if(outBufferOffset >= outBufferLength) {
                        flushOutBuffer();
                    }
                    outBuffer[outBufferOffset++] = lineSep.charAt(i);
                }
            }
        } finally {
            this.mod = 0;
            this.buf = 0;
        }
    }

    /**
     * This is where stuff stored  in outBuffer is actually written to
     * out.
     **/
    private final void flushOutBuffer() throws IOException {
        out.write(outBuffer, 0, outBufferOffset);
        outBufferOffset = 0;
    }

    /**
     * Flushes      all     buffered  data     to     the   underlying
     * <tt>OutputStream</tt> or <tt>Writer</tt>.  If the length of the
     * buffered data  is not a multiple of  three bytes, the data will
     * be padded  with  '=' characters as   necessary.  The underlying
     * stream is also <tt>flush</tt>ed.
     * 
     * <p>RFC 1521 specifies that  padding with '=' characters is only
     * performed  at the end  of the data.   Users should note that if
     * flush is forced to pad  output data, non-compliant encoding may
     * result if this happens at any time other than at the end of the
     * data.
     *
     * <p>[EMAIL PROTECTED]  Base64InputStream} is capable   of reading data 
that
     * has been '='   padded at places other  than  at the end of  the
     * input.  However, to  ensure compliant encoding,  <tt>flush</tt>
     * should      only   be    invoked   once    per      instance of
     * <tt>Base64OutputStream</tt>.
     *
     * @see #close
     *
     * @exception  IOException  if  thrown    during writing   to  the
     * underlying stream, or if this stream has been closed
     **/
    public synchronized void flush() throws IOException {
        if(closed) {
            throw new IOException("stream has been closed");
        }
        
        if(mod != 0) {
            flush24(mod);
        }
        
        flushOutBuffer();

        out.flush();
    }

    /**
     * Flushes the stream and closes it to prevent further writing.
     * @see #flush
     * @exception IOException if the stream has already been closed
     **/
    public synchronized void close() throws IOException {
        if(!closed) {
            try {
                flush();
            } finally {
                closed = true;
            }
        }
    }

    public static void main(String[] args) throws IOException {
        OutputStream o = new Base64OutputStream(System.out, 1024);

        byte[] buf = new byte[1000]; int len;
        java.io.InputStream r = new java.io.FileInputStream(args[0]);
        while((len = r.read(buf, 0, 1000)) != -1) o.write(buf, 0, len);
        o.flush();
        o.close();
        System.out.println();

        //for(int i=0; i<alphabet.length; i++) {
        //    System.out.println(i+"="+alphabet[i]+","+((int)alphabet[i]));
        //}
    }
}

/**
 * Copyright 2001 Peter Davis.
 **/

package cx.pdavis.xerial;

import java.io.InputStream;
import java.io.Reader;
import java.io.InputStreamReader;
import java.io.IOException;

/**
 * Reads binary data  encoded  in <tt>base64</tt>.  Each  character in
 * the input is an ASCII value in the range A-Z, a-z, 0-9, '+', or '/'
 * that  maps to one of  64 binary values (6  bits).   The 24 combined
 * bits  of four  input characters  are combined  to form three binary
 * bytes.   Input  quadruplets  may be padded  with   one  or two  '='
 * characters in case the original  data was not  a multiple of  three
 * bytes          in           length.              See             <a
 * href="http://www.faqs.org/rfcs/rfc1521.html";>RFC  1521,     section
 * 5.2"</a> for the full Base64 encoding specification.
 *
 * <p>As specified  by RFC 1521,  a <tt>Base64InputStream</tt> ignores
 * all whitespace  in  the input.  Non-whitespace characters  that are
 * not  in    one         of   the   above     ranges      cause    an
 * <tt>IllegalBase64QuadrupletException</tt>, since   they    are most
 * likely  the   result  of  corruption  or  a   transmition  failure.
 * <tt>Base64InputStream</tt>s  do not  support   ignoring of  illegal
 * characters as this is optional behavior according to RFC 1521.
 *
 * <p>Input that is not properly padded  to a multiple of four sextets
 * with          '='        characters       will     cause         an
 * <tt>IllegalBase64PaddingException</tt> to be thrown.
 *
 * <p>In a  slight deviation from RFC 1521, <tt>Base64InputStream</tt>
 * ignores '='  padding that does not occur  at the  end of the input.
 * See [EMAIL PROTECTED] Base64OutputStream#flush} for  a description of why 
this
 * is sometimes necessary.  This modification remains fully compatible
 * with compliant encoders.
 *
 * @see Base64OutputStream
 * @see IllegalBase64QuadrupletException
 * @see IllegalBase64PaddingException
 * @see  <a  href="http://www.faqs.org/rfcs/rfc1521.html";>RFC    1521,
 * section 5.2"</a>
 **/
public class Base64InputStream extends InputStream {

    private final Reader in;

    private char[] buf;

    private byte[] byteBuf;
    private int byteBufOff;
    private int byteBufFilled;

    private boolean eof;

    private boolean closed;

    // Each  index of reverseAlphabet is  the integer  value of one of
    // the valid input characters  and   is set to that    character's
    // corresponding  binary value.  Characters  that  are illegal but
    // still in the range of 0 to 123 are mapped to '-1'.
    static final int[] reverseAlphabet = new int[((int)'z') + 1]; // new 
int[123];
    static {
        for(int i=0; i<(((int)'z')+1); i++) reverseAlphabet[i] = -1;
        for(int i=0, il=Base64OutputStream.alphabet.length; i<il; i++) { // i < 
64
            reverseAlphabet[Base64OutputStream.alphabet[i]] = i;
        }
    }

    /**
     * Creates a new Base64InputStream that reads input from the given
     * InputStream.
     *
     * @param is the InputStream from which this stream will read data
     **/
    public Base64InputStream(InputStream is) {
        this.in = new QuadrupletReader(new InputStreamReader(is), 1024);
        this.buf = new char[1024];
        this.byteBuf = new byte[768];
    }

    /**
     * Creates a new Base64InputStream that reads input from the given
     * Reader.
     *
     * @param is the Reader from which this stream will read data
     **/
    public Base64InputStream(Reader r) {
        this.in = new QuadrupletReader(r, 1024);
        this.buf = new char[1024];
        this.byteBuf = new byte[768];
    }

    /**
     * Creates a new Base64InputStream that reads input from the given
     * InputStream.
     *
     * @param is the InputStream from which this stream will read data
     * @param  bufferSize the size  of   the internal input buffer  in
     * characters
     * @exception IllegalArgumentException if bufferSize is less than 0
     **/
    public Base64InputStream(InputStream is, int bufferSize) {
        if(bufferSize < 0) {
            throw new IllegalArgumentException("bufferSize < 0");
        } else if(bufferSize < 8) {
            // buffer size <= 4 makes bogus shit happen
            bufferSize = 8;
        } else {
            bufferSize += 4 - (bufferSize % 4);
        }
        this.in = new QuadrupletReader(new InputStreamReader(is), bufferSize);
        this.buf = new char[bufferSize];
        this.byteBuf = new byte[(int)(bufferSize * 0.75f)];
    }

    /**
     * Creates a new Base64InputStream that reads input from the given
     * Reader.
     *
     * @param is the Reader from which this stream will read data
     * @param bufferSize   the size of  the  internal input  buffer in
     * characters
     * @exception IllegalArgumentException if bufferSize is less than 0
     **/
    public Base64InputStream(Reader r, int bufferSize) {
        if(bufferSize < 0) {
            throw new IllegalArgumentException("bufferSize < 0");
        } else if(bufferSize < 8) {
            // buffer size <= 4 makes bogus shit happen
            bufferSize = 8;
        } else {
            bufferSize += 4 - (bufferSize % 4);
        }
        this.in = new QuadrupletReader(r, bufferSize);
        this.buf = new char[bufferSize];
        this.byteBuf = new byte[(int)(bufferSize * 0.75f)];
    }

    /**
     * Decodes and returns a single byte.  If the end of the input has
     * been reached, -1 is returned.
     *
     * @exception    IllegalBase64QuadrupletException     if   illegal
     * non-whitespace characters are found in the input
     * @exception IllegalBase64PaddingException if the end of input is
     * reached and determined to not be correctly padded
     * @exception  IOException if thrown  by the underlying reader, or
     * if this stream has been closed
     *
     * @return the byte  read,  or -1 if   the end of input has   been
     * reached
     **/
    public synchronized int read() throws IOException {
        if(closed) {
            throw new IOException("stream has been closed");
        }
        
        if(byteBufOff >= byteBufFilled) {
            byteBufOff = byteBufFilled = 0;
            fillBuffer();
        }
        return eof ? -1 : byteBuf[byteBufOff++] & 0xFF;
    }

    /**
     * Attempts to fill the internal  buffer with as much decoded data
     * as possible.
     **/
    private final void fillBuffer() throws IOException {
        int read;

        if(eof = (read = in.read(buf)) == -1) {
            return;
        }

        if(read % 4 != 0) {
            throw new IllegalBase64PaddingException("input length must be 
multiple of 4");
        }
        
        for(int i=0; i<read; i+=4) {
            int val1, val2, val3, val4;
            try {
                int num = 4;
                val1 = reverseAlphabet[buf[i]];
                val2 = reverseAlphabet[buf[i + 1]];
                val3 = reverseAlphabet[buf[i + 2]];
                val4 = reverseAlphabet[buf[i + 3]];
                if(val4 == -1 && buf[i + 3] == '=') {
                    num = (val3 == -1 && buf[i + 2] == '=') ? 2 : 3;
                } else if(val1 == -1 || val2 == -1 || val3 == -1 || val4 == -1) 
{
                    throw new 
IllegalBase64QuadrupletException(String.valueOf(buf, i, 4));
                }

                // 0 0 0 0 0 0 0 0   0 0 0 0 0 0 0 0   0 0 0 0 0 0 0 0
                // | | | | | | \ \  / / / /  | | | |  / / / / / / / /
                // 0 0 0 0 0 0  0 0 0 0 0 0  0 0 0 0 0 0  0 0 0 0 0 0
                switch(num) {
                case 4:
                    byteBuf[byteBufFilled++] = (byte)((val1 << 2) | (val2 >>> 
4));
                    byteBuf[byteBufFilled++] = (byte)(((val2 & 0xF) << 4) | 
(val3 >>> 2));
                    byteBuf[byteBufFilled++] = (byte)(((val3 & 0x3) << 6) | 
val4);
                    break;
                case 3:
                    byteBuf[byteBufFilled++] = (byte)((val1 << 2) | (val2 >>> 
4));
                    byteBuf[byteBufFilled++] = (byte)(((val2 & 0xF) << 4) | 
(val3 >>> 2));
                    break;
                case 2:
                    byteBuf[byteBufFilled++] = (byte)((val1 << 2) | (val2 >>> 
4));
                    break;
                default:
                    throw new 
IllegalBase64QuadrupletException(String.valueOf(buf, i, 4));
                }
            } catch(ArrayIndexOutOfBoundsException aioobe) {
                aioobe.printStackTrace();
                throw new IllegalBase64QuadrupletException(String.valueOf(buf, 
i, 4));
            }
        }
    }

    /**
     * Used to filter out whitespace  and to always return a  multiple
     * of four bytes.  The no-arg read() method should never be used.
     **/
    private static class QuadrupletReader extends Reader {
        private final char[] buf;
        
        private final char[] quad = new char[4];
        private int leftovers;
        
        private final Reader in;
        
        public QuadrupletReader(Reader r, int bufSize) {
            this.in = r;
            this.buf = new char[bufSize];
        }

        public int read(char[] buf, int off, int len) throws IOException {
            // len -  off  must  be  <=  the   bufSize  given in   the
            // constructor.   Since  this class is  only  used  within
            // Base64InputStream, this isn't a problem.

            final char[] realBuf = this.buf;
            final char[] quad = this.quad;
            
            final int read = in.read(realBuf);
            int actual = off;
            char c;

            for(int i=0; i < read; i++) {
                if(!Character.isWhitespace(c = realBuf[i])) {
                    quad[leftovers++] = c;
                    if(leftovers == 4) {
                        buf[actual++] = quad[0];
                        buf[actual++] = quad[1];
                        buf[actual++] = quad[2];
                        buf[actual++] = quad[3];
                        leftovers = 0;
                    }
                }
            }
            
            if(read == -1) {
                // nothing read from in, but there might be
                // leftovers from previous invokations
                if(leftovers != 0) {
                    for(int i=leftovers; i<4; i++) {
                        buf[actual++] = quad[i];
                    }
                    leftovers = 0;
                } else {
                    return -1;
                }
            }
                
            return actual - off;
        }

        public void close() throws IOException {
            in.close();
        }
    }

    public synchronized void close() throws IOException {
        closed = true;
        buf = null;
        byteBuf = null;
    }


    public static void main(String[] args) throws IOException {
        int val; InputStream is;

        if(args.length > 1) {
            is = new java.io.FileInputStream(args[1]);
            while((val = is.read()) != -1) {
                System.out.print(',');
                System.out.print(Integer.toString(val));
            }

            System.out.println("\nActual Results:");
        }
        
        is = new Base64InputStream(new java.io.FileInputStream(args[0]), 1);
        while((val = is.read()) != -1) {
            System.out.print(',');
            System.out.print(Integer.toString(val));
        }
    }

}
--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to