You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2011/01/26 16:22:39 UTC

svn commit: r1063759 - /commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base32.java

Author: sebb
Date: Wed Jan 26 15:22:39 2011
New Revision: 1063759

URL: http://svn.apache.org/viewvc?rev=1063759&view=rev
Log:
Javadoc

Modified:
    commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base32.java

Modified: commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base32.java
URL: http://svn.apache.org/viewvc/commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base32.java?rev=1063759&r1=1063758&r2=1063759&view=diff
==============================================================================
--- commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base32.java (original)
+++ commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base32.java Wed Jan 26 15:22:39 2011
@@ -17,11 +17,6 @@
 
 package org.apache.commons.codec.binary;
 
-import org.apache.commons.codec.BinaryDecoder;
-import org.apache.commons.codec.BinaryEncoder;
-import org.apache.commons.codec.DecoderException;
-import org.apache.commons.codec.EncoderException;
-
 /**
  * Provides Base32 encoding and decoding as defined by RFC 4648.
  * 
@@ -46,11 +41,7 @@ import org.apache.commons.codec.EncoderE
  * @since 1.5
  * @version $Revision$
  */
-public class Base32 implements BinaryEncoder, BinaryDecoder {
-
-    private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2;
-
-    private static final int DEFAULT_BUFFER_SIZE = 8192;
+public class Base32 extends BasedCodec {
 
     /**
      * BASE32 characters are 5 bits in length. 
@@ -61,31 +52,6 @@ public class Base32 implements BinaryEnc
     private static final int BYTES_PER_UNENCODED_BLOCK = 5;
     private static final int BYTES_PER_ENCODED_BLOCK = 8;
 
-
-    /**
-     *  MIME chunk size per RFC 2045 section 6.8.
-     * 
-     * <p>
-     * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
-     * equal signs.
-     * </p>
-     * 
-     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
-     */
-    public static final int MIME_CHUNK_SIZE = 76;
-
-    /**
-     * PEM chunk size per RFC 1421 section 4.3.2.4.
-     * 
-     * <p>
-     * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
-     * equal signs.
-     * </p>
-     * 
-     * @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421 section 4.3.2.4</a>
-     */
-    public static final int PEM_CHUNK_SIZE = 64;
-
     /**
      * Chunk separator per RFC 2045 section 2.1.
      *
@@ -168,9 +134,8 @@ public class Base32 implements BinaryEnc
      * Encode table to use.
      */
     private final byte[] encodeTable;
-    
-    private final byte[] decodeTable; // need different decode table to support Hex version
 
+    private final byte[] decodeTable;
     /**
      * Line length for encoding. Not used when decoding. A value of zero or less implies no chunking of the Base32
      * encoded data.
@@ -197,45 +162,6 @@ public class Base32 implements BinaryEnc
     private final int encodeSize;
 
     /**
-     * Buffer for streaming.
-     */
-    private byte[] buffer;
-
-    /**
-     * Position where next character should be written in the buffer.
-     */
-    private int pos;
-
-    /**
-     * Position where next character should be read from the buffer.
-     */
-    private int readPos;
-
-    /**
-     * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to
-     * make sure each encoded line never goes beyond lineLength (if lineLength > 0).
-     */
-    private int currentLinePos;
-
-    /**
-     * Writes to the buffer only occur after every 5 reads when encoding, and every 8 reads when decoding. This variable
-     * helps track that.
-     */
-    private int modulus;
-
-    /**
-     * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this Base32 object becomes useless,
-     * and must be thrown away.
-     */
-    private boolean eof;
-
-    /**
-     * Place holder for the 8 bytes we're dealing with for our Base32 logic. Bitwise operations store and extract the
-     * Base32 encoding or decoding from this variable.
-     */
-    private long x; // 64 bits; enough for 40 bits i.e. 5 octets unencoded
-
-    /**
      * Creates a Base32 codec used for decoding and encoding.
      * <p>
      * When encoding the line length is 0 (no chunking).
@@ -338,274 +264,13 @@ public class Base32 implements BinaryEnc
     }
 
     /**
-     * Returns true if this Base32 object has buffered data for reading.
-     * 
-     * @return true if there is Base32 object still available for reading.
-     */
-    boolean hasData() {
-        return this.buffer != null;
-    }
-
-    /**
-     * Returns the amount of buffered data available for reading.
-     * 
-     * @return The amount of buffered data available for reading.
-     */
-    int avail() {
-        return buffer != null ? pos - readPos : 0;
-    }
-
-    /** Doubles our buffer. */
-    private void resizeBuffer() {
-        if (buffer == null) {
-            buffer = new byte[DEFAULT_BUFFER_SIZE];
-            pos = 0;
-            readPos = 0;
-        } else {
-            byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR];
-            System.arraycopy(buffer, 0, b, 0, buffer.length);
-            buffer = b;
-        }
-    }
-
-    /**
-     * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail
-     * bytes. Returns how many bytes were actually extracted.
-     * 
-     * @param b
-     *            byte[] array to extract the buffered data into.
-     * @param bPos
-     *            position in byte[] array to start extraction at.
-     * @param bAvail
-     *            amount of bytes we're allowed to extract. We may extract fewer (if fewer are available).
-     * @return The number of bytes successfully extracted into the provided byte[] array.
-     */
-    int readResults(byte[] b, int bPos, int bAvail) {
-        if (buffer != null) {
-            int len = Math.min(avail(), bAvail);
-            System.arraycopy(buffer, readPos, b, bPos, len);
-            readPos += len;
-            if (readPos >= pos) {
-                buffer = null;
-            }
-            return len;
-        }
-        return eof ? -1 : 0;
-    }
-
-    /**
-     * <p>
-     * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with
-     * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last
-     * remaining bytes (if not multiple of 5).
-     * </p>
-     * 
-     * @param in
-     *            byte[] array of binary data to Base32 encode.
-     * @param inPos
-     *            Position to start reading data from.
-     * @param inAvail
-     *            Amount of bytes available from input for encoding.
-     */
-    void encode(byte[] in, int inPos, int inAvail) {
-        if (eof) {
-            return;
-        }
-        // inAvail < 0 is how we're informed of EOF in the underlying data we're
-        // encoding.
-        if (inAvail < 0) {
-            eof = true;
-            if (buffer == null || buffer.length - pos < encodeSize) {
-                resizeBuffer();
-            }
-            switch (modulus) { // % 5
-                case 1 : // Only 1 octet; take top 5 bits then remainder
-                    buffer[pos++] = encodeTable[(int)(x >> 3) & MASK_5BITS]; // 8-1*5 = 3
-                    buffer[pos++] = encodeTable[(int)(x << 2) & MASK_5BITS]; // 5-3=2
-                    buffer[pos++] = PAD;
-                    buffer[pos++] = PAD;
-                    buffer[pos++] = PAD;
-                    buffer[pos++] = PAD;
-                    buffer[pos++] = PAD;
-                    buffer[pos++] = PAD;
-                    break;
-
-                case 2 : // 2 octets = 16 bits to use
-                    buffer[pos++] = encodeTable[(int)(x >> 11) & MASK_5BITS]; // 16-1*5 = 11
-                    buffer[pos++] = encodeTable[(int)(x >>  6) & MASK_5BITS]; // 16-2*5 = 6
-                    buffer[pos++] = encodeTable[(int)(x >>  1) & MASK_5BITS]; // 16-3*5 = 1
-                    buffer[pos++] = encodeTable[(int)(x <<  4) & MASK_5BITS]; // 5-1 = 4
-                    buffer[pos++] = PAD;
-                    buffer[pos++] = PAD;
-                    buffer[pos++] = PAD;
-                    buffer[pos++] = PAD;
-                    break;
-                case 3 : // 3 octets = 24 bits to use
-                    buffer[pos++] = encodeTable[(int)(x >> 19) & MASK_5BITS]; // 24-1*5 = 19
-                    buffer[pos++] = encodeTable[(int)(x >> 14) & MASK_5BITS]; // 24-2*5 = 14
-                    buffer[pos++] = encodeTable[(int)(x >>  9) & MASK_5BITS]; // 24-3*5 = 9
-                    buffer[pos++] = encodeTable[(int)(x >>  4) & MASK_5BITS]; // 24-4*5 = 4
-                    buffer[pos++] = encodeTable[(int)(x <<  1) & MASK_5BITS]; // 5-4 = 1
-                    buffer[pos++] = PAD;
-                    buffer[pos++] = PAD;
-                    buffer[pos++] = PAD;
-                    break;
-                case 4 : // 4 octets = 32 bits to use
-                    buffer[pos++] = encodeTable[(int)(x >> 27) & MASK_5BITS]; // 32-1*5 = 27
-                    buffer[pos++] = encodeTable[(int)(x >> 22) & MASK_5BITS]; // 32-2*5 = 22
-                    buffer[pos++] = encodeTable[(int)(x >> 17) & MASK_5BITS]; // 32-3*5 = 17
-                    buffer[pos++] = encodeTable[(int)(x >> 12) & MASK_5BITS]; // 32-4*5 = 12
-                    buffer[pos++] = encodeTable[(int)(x >>  7) & MASK_5BITS]; // 32-5*5 =  7
-                    buffer[pos++] = encodeTable[(int)(x >>  2) & MASK_5BITS]; // 32-6*5 =  2
-                    buffer[pos++] = encodeTable[(int)(x <<  3) & MASK_5BITS]; // 5-2 = 3
-                    buffer[pos++] = PAD;
-                    break;
-            }
-            // Don't want to append the CRLF two times in a row, so make sure previous
-            // character is not from CRLF!
-            byte b = lineSeparator[lineSeparator.length - 1];
-            if (lineLength > 0 && pos > 0 && buffer[pos-1] != b) {
-                System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length);
-                pos += lineSeparator.length;
-            }
-        } else {
-            for (int i = 0; i < inAvail; i++) {
-                if (buffer == null || buffer.length - pos < encodeSize) {
-                    resizeBuffer();
-                }
-                modulus = (++modulus) % BITS_PER_ENCODED_CHAR;
-                int b = in[inPos++];
-                if (b < 0) {
-                    b += 256;
-                }
-                x = (x << 8) + b; // ??
-                if (0 == modulus) { // we have enough bytes to create our output 
-                    buffer[pos++] = encodeTable[(int)(x >> 35) & MASK_5BITS];
-                    buffer[pos++] = encodeTable[(int)(x >> 30) & MASK_5BITS];
-                    buffer[pos++] = encodeTable[(int)(x >> 25) & MASK_5BITS];
-                    buffer[pos++] = encodeTable[(int)(x >> 20) & MASK_5BITS];
-                    buffer[pos++] = encodeTable[(int)(x >> 15) & MASK_5BITS];
-                    buffer[pos++] = encodeTable[(int)(x >> 10) & MASK_5BITS];
-                    buffer[pos++] = encodeTable[(int)(x >> 5) & MASK_5BITS];
-                    buffer[pos++] = encodeTable[(int)x & MASK_5BITS];
-                    currentLinePos += BYTES_PER_ENCODED_BLOCK;
-                    if (lineLength > 0 && lineLength <= currentLinePos) {
-                        System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length);
-                        pos += lineSeparator.length;
-                        currentLinePos = 0;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * <p>
-     * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once
-     * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1"
-     * call is not necessary when decoding, but it doesn't hurt, either.
-     * </p>
-     * <p>
-     * Ignores all non-Base32 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are
-     * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in,
-     * garbage-out philosophy: it will not check the provided data for validity.
-     * </p>
-     * 
-     * @param in
-     *            byte[] array of ascii data to Base32 decode.
-     * @param inPos
-     *            Position to start reading data from.
-     * @param inAvail
-     *            Amount of bytes available from input for encoding.
-     *
-     * Output is written to {@link #buffer} as 8-bit octets, using {@link pos} as the buffer position
-     */
-    void decode(byte[] in, int inPos, int inAvail) { // package protected for access from I/O streams
-        if (eof) {
-            return;
-        }
-        if (inAvail < 0) {
-            eof = true;
-        }
-        for (int i = 0; i < inAvail; i++) {
-            if (buffer == null || buffer.length - pos < decodeSize) {
-                resizeBuffer();
-            }
-            byte b = in[inPos++];
-            if (b == PAD) {
-                // We're done.
-                eof = true;
-                break;
-            } else {
-                if (b >= 0 && b < this.decodeTable.length) {
-                    int result = this.decodeTable[b];
-                    if (result >= 0) {
-                        modulus = (++modulus) % BYTES_PER_ENCODED_BLOCK;
-                        x = (x << BITS_PER_ENCODED_CHAR) + result; // collect decoded bytes
-                        if (modulus == 0) { // we can output the 5 bytes
-                            buffer[pos++] = (byte) ((x >> 32) & MASK_8BITS);
-                            buffer[pos++] = (byte) ((x >> 24) & MASK_8BITS);
-                            buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
-                            buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
-                            buffer[pos++] = (byte) (x & MASK_8BITS);
-                        }
-                    }
-                }
-            }
-        }
-
-        // Two forms of EOF as far as Base32 decoder is concerned: actual
-        // EOF (-1) and first time '=' character is encountered in stream.
-        // This approach makes the '=' padding characters completely optional.
-        if (eof && modulus != 0) {
-            if (buffer == null || buffer.length - pos < decodeSize) {
-                resizeBuffer();
-            }
-
-            //  we ignore partial bytes, i.e. only multiples of 8 count
-            switch (modulus) {
-                case 2 : // 10 bits, drop 2 and output one byte
-                    buffer[pos++] = (byte) ((x >> 2) & MASK_8BITS);
-                    break;
-                case 3 : // 15 bits, drop 7 and output 1 byte
-                    buffer[pos++] = (byte) ((x >> 7) & MASK_8BITS);
-                    break;
-                case 4 : // 20 bits = 2*8 + 4
-                    x = x >> 4; // drop 4 bits
-                    buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
-                    buffer[pos++] = (byte) ((x) & MASK_8BITS);
-                    break;
-                case 5 : // 25bits = 3*8 + 1
-                    x = x >> 1;
-                    buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
-                    buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
-                    buffer[pos++] = (byte) ((x) & MASK_8BITS);
-                    break;
-                case 6 : // 30bits = 3*8 + 6
-                    x = x >> 6;
-                    buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
-                    buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
-                    buffer[pos++] = (byte) ((x) & MASK_8BITS);
-                    break;
-                case 7 : // 35 = 4*8 +3
-                    x = x >> 3;
-                    buffer[pos++] = (byte) ((x >> 24) & MASK_8BITS);
-                    buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
-                    buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
-                    buffer[pos++] = (byte) ((x) & MASK_8BITS);
-                    break;
-            }
-        }
-    }
-
-    /**
      * Returns whether or not the <code>octet</code> is in the Base32 alphabet.
      * 
      * @param octet
      *            The value to test
      * @return <code>true</code> if the value is defined in the the Base32 alphabet (or pad), <code>false</code> otherwise.
      */
-    public static boolean isBase32(byte octet) {
+    public boolean isBase32(byte octet) {
         return octet == PAD || (octet >= 0 && octet < BASE32_DECODE_TABLE.length && BASE32_DECODE_TABLE[octet] != -1);
     }
 
@@ -616,7 +281,7 @@ public class Base32 implements BinaryEnc
      *            The value to test
      * @return <code>true</code> if the value is defined in the the Base32 Hex alphabet (or pad), <code>false</code> otherwise.
      */
-    public static boolean isBase32Hex(byte octet) {
+    public boolean isBase32Hex(byte octet) {
         return octet == PAD || (octet >= 0 && octet < BASE32HEX_DECODE_TABLE.length && BASE32HEX_DECODE_TABLE[octet] != -1);
     }
 
@@ -629,7 +294,7 @@ public class Base32 implements BinaryEnc
      * @return <code>true</code> if all characters in the String are valid characters in the Base32 alphabet or if
      *         the String is empty; <code>false</code>, otherwise
      */
-    public static boolean isBase32(String base32) {
+    public boolean isBase32(String base32) {
         return isBase32(StringUtils.getBytesUtf8(base32));
     }
 
@@ -642,7 +307,7 @@ public class Base32 implements BinaryEnc
      * @return <code>true</code> if all bytes are valid characters in the Base32 alphabet or if the byte array is empty;
      *         <code>false</code>, otherwise
      */    
-    public static boolean isBase32(byte[] arrayOctet) {
+    public boolean isBase32(byte[] arrayOctet) {
         for (int i = 0; i < arrayOctet.length; i++) {
             if (!isBase32(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) {
                 return false;
@@ -652,14 +317,14 @@ public class Base32 implements BinaryEnc
     }
     
     /**
-     * Tests a given byte array to see if it contains only valid characters within the Base32 alphabet.
+     * Tests a given byte array to see if it contains any characters within the Base32 alphabet.
      * Does not allow white-space.
      * 
      * @param arrayOctet
      *            byte array to test
      * @return <code>true</code> if any byte is a valid character in the Base32 alphabet; <code>false</code> otherwise
      */
-    private static boolean containsBase32Byte(byte[] arrayOctet) {
+    private boolean containsBase32Byte(byte[] arrayOctet) {
         for (int i = 0; i < arrayOctet.length; i++) {
             if (isBase32(arrayOctet[i])) {
                 return true;
@@ -713,56 +378,6 @@ public class Base32 implements BinaryEnc
     }
 
     /**
-     * Decodes an Object using the Base32 algorithm. This method is provided in order to satisfy the requirements of the
-     * Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[] or String.
-     * 
-     * @param pObject
-     *            Object to decode
-     * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] or String supplied.
-     * @throws DecoderException
-     *             if the parameter supplied is not of type byte[]
-     */
-    public Object decode(Object pObject) throws DecoderException {        
-        if (pObject instanceof byte[]) {
-            return decode((byte[]) pObject);
-        } else if (pObject instanceof String) {
-            return decode((String) pObject);
-        } else {
-            throw new DecoderException("Parameter supplied to Base32 decode is not a byte[] or a String");
-        }
-    }
-
-    /**
-     * Decodes a String containing characters in the Base32 alphabet.
-     *
-     * @param pArray
-     *            A String containing Base32 character data
-     * @return a byte array containing binary data
-     */
-    public byte[] decode(String pArray) {
-        return decode(StringUtils.getBytesUtf8(pArray));
-    }
-
-    /**
-     * Decodes a byte[] containing characters in the Base32 alphabet.
-     * 
-     * @param pArray
-     *            A byte array containing Base32 character data
-     * @return a byte array containing binary data
-     */
-    public byte[] decode(byte[] pArray) {
-        reset();
-        if (pArray == null || pArray.length == 0) {
-            return pArray;
-        }
-        decode(pArray, 0, pArray.length);
-        decode(pArray, 0, -1); // Notify decoder of EOF.
-        byte[] result = new byte[pos];
-        readResults(result, 0, result.length);
-        return result;
-    }
-
-    /**
      * Encodes binary data using the Base32 algorithm, optionally chunking the output into 76 character blocks.
      * 
      * @param binaryData
@@ -897,113 +512,239 @@ public class Base32 implements BinaryEnc
     }
 
     /**
-     * Checks if a byte value is whitespace or not.
-     * 
-     * @param byteToCheck
-     *            the byte to check
-     * @return true if byte is whitespace, false otherwise
-     */
-    private static boolean isWhiteSpace(byte byteToCheck) {
-        switch (byteToCheck) {
-            case ' ' :
-            case '\n' :
-            case '\r' :
-            case '\t' :
-                return true;
-            default :
-                return false;
-        }
-    }
-
-    // Implementation of the Encoder Interface
-
-    /**
-     * Encodes an Object using the Base32 algorithm. This method is provided in order to satisfy the requirements of the
-     * Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[].
+     * <p>
+     * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with
+     * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last
+     * remaining bytes (if not multiple of 5).
+     * </p>
      * 
-     * @param pObject
-     *            Object to encode
-     * @return An object (of type byte[]) containing the Base32 encoded data which corresponds to the byte[] supplied.
-     * @throws EncoderException
-     *             if the parameter supplied is not of type byte[]
+     * @param in
+     *            byte[] array of binary data to Base32 encode.
+     * @param inPos
+     *            Position to start reading data from.
+     * @param inAvail
+     *            Amount of bytes available from input for encoding.
      */
-    public Object encode(Object pObject) throws EncoderException {
-        if (!(pObject instanceof byte[])) {
-            throw new EncoderException("Parameter supplied to Base32 encode is not a byte[]");
+    void encode(byte[] in, int inPos, int inAvail) { // package protected for access from I/O streams
+        if (eof) {
+            return;
         }
-        return encode((byte[]) pObject);
-    }
-
-    /**
-     * Encodes a byte[] containing binary data, into a String containing characters in the Base32 alphabet.
-     *
-     * @param pArray
-     *            a byte array containing binary data
-     * @return A String containing only Base32 character data
-     */    
-    public String encodeToString(byte[] pArray) {
-        return StringUtils.newStringUtf8(encode(pArray));
-    }
-
-    /**
-     * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base32 alphabet.
-     * 
-     * @param pArray
-     *            a byte array containing binary data
-     * @return A byte array containing only Base32 character data
-     */
-    public byte[] encode(byte[] pArray) {
-        reset();        
-        if (pArray == null || pArray.length == 0) {
-            return pArray;
+        // inAvail < 0 is how we're informed of EOF in the underlying data we're
+        // encoding.
+        if (inAvail < 0) {
+            eof = true;
+            if (buffer == null || buffer.length - pos < encodeSize) {
+                resizeBuffer();
+            }
+            switch (modulus) { // % 5
+                case 1 : // Only 1 octet; take top 5 bits then remainder
+                    buffer[pos++] = encodeTable[(int)(x >> 3) & MASK_5BITS]; // 8-1*5 = 3
+                    buffer[pos++] = encodeTable[(int)(x << 2) & MASK_5BITS]; // 5-3=2
+                    buffer[pos++] = PAD;
+                    buffer[pos++] = PAD;
+                    buffer[pos++] = PAD;
+                    buffer[pos++] = PAD;
+                    buffer[pos++] = PAD;
+                    buffer[pos++] = PAD;
+                    break;
+    
+                case 2 : // 2 octets = 16 bits to use
+                    buffer[pos++] = encodeTable[(int)(x >> 11) & MASK_5BITS]; // 16-1*5 = 11
+                    buffer[pos++] = encodeTable[(int)(x >>  6) & MASK_5BITS]; // 16-2*5 = 6
+                    buffer[pos++] = encodeTable[(int)(x >>  1) & MASK_5BITS]; // 16-3*5 = 1
+                    buffer[pos++] = encodeTable[(int)(x <<  4) & MASK_5BITS]; // 5-1 = 4
+                    buffer[pos++] = PAD;
+                    buffer[pos++] = PAD;
+                    buffer[pos++] = PAD;
+                    buffer[pos++] = PAD;
+                    break;
+                case 3 : // 3 octets = 24 bits to use
+                    buffer[pos++] = encodeTable[(int)(x >> 19) & MASK_5BITS]; // 24-1*5 = 19
+                    buffer[pos++] = encodeTable[(int)(x >> 14) & MASK_5BITS]; // 24-2*5 = 14
+                    buffer[pos++] = encodeTable[(int)(x >>  9) & MASK_5BITS]; // 24-3*5 = 9
+                    buffer[pos++] = encodeTable[(int)(x >>  4) & MASK_5BITS]; // 24-4*5 = 4
+                    buffer[pos++] = encodeTable[(int)(x <<  1) & MASK_5BITS]; // 5-4 = 1
+                    buffer[pos++] = PAD;
+                    buffer[pos++] = PAD;
+                    buffer[pos++] = PAD;
+                    break;
+                case 4 : // 4 octets = 32 bits to use
+                    buffer[pos++] = encodeTable[(int)(x >> 27) & MASK_5BITS]; // 32-1*5 = 27
+                    buffer[pos++] = encodeTable[(int)(x >> 22) & MASK_5BITS]; // 32-2*5 = 22
+                    buffer[pos++] = encodeTable[(int)(x >> 17) & MASK_5BITS]; // 32-3*5 = 17
+                    buffer[pos++] = encodeTable[(int)(x >> 12) & MASK_5BITS]; // 32-4*5 = 12
+                    buffer[pos++] = encodeTable[(int)(x >>  7) & MASK_5BITS]; // 32-5*5 =  7
+                    buffer[pos++] = encodeTable[(int)(x >>  2) & MASK_5BITS]; // 32-6*5 =  2
+                    buffer[pos++] = encodeTable[(int)(x <<  3) & MASK_5BITS]; // 5-2 = 3
+                    buffer[pos++] = PAD;
+                    break;
+            }
+            // Don't want to append the CRLF two times in a row, so make sure previous
+            // character is not from CRLF!
+            byte b = lineSeparator[lineSeparator.length - 1];
+            if (lineLength > 0 && pos > 0 && buffer[pos-1] != b) {
+                System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length);
+                pos += lineSeparator.length;
+            }
+        } else {
+            for (int i = 0; i < inAvail; i++) {
+                if (buffer == null || buffer.length - pos < encodeSize) {
+                    resizeBuffer();
+                }
+                modulus = (++modulus) % BITS_PER_ENCODED_CHAR;
+                int b = in[inPos++];
+                if (b < 0) {
+                    b += 256;
+                }
+                x = (x << 8) + b; // ??
+                if (0 == modulus) { // we have enough bytes to create our output 
+                    buffer[pos++] = encodeTable[(int)(x >> 35) & MASK_5BITS];
+                    buffer[pos++] = encodeTable[(int)(x >> 30) & MASK_5BITS];
+                    buffer[pos++] = encodeTable[(int)(x >> 25) & MASK_5BITS];
+                    buffer[pos++] = encodeTable[(int)(x >> 20) & MASK_5BITS];
+                    buffer[pos++] = encodeTable[(int)(x >> 15) & MASK_5BITS];
+                    buffer[pos++] = encodeTable[(int)(x >> 10) & MASK_5BITS];
+                    buffer[pos++] = encodeTable[(int)(x >> 5) & MASK_5BITS];
+                    buffer[pos++] = encodeTable[(int)x & MASK_5BITS];
+                    currentLinePos += BYTES_PER_ENCODED_BLOCK;
+                    if (lineLength > 0 && lineLength <= currentLinePos) {
+                        System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length);
+                        pos += lineSeparator.length;
+                        currentLinePos = 0;
+                    }
+                }
+            }
         }
-        encode(pArray, 0, pArray.length);
-        encode(pArray, 0, -1); // Notify encoder of EOF.
-        byte[] buf = new byte[pos - readPos];
-        readResults(buf, 0, buf.length);
-        return buf;
     }
 
     /**
-     * Pre-calculates the amount of space needed to Base32-encode the supplied array.
-     *
-     * @param pArray byte[] array which will later be encoded
-     * @param chunkSize line-length of the output (<= 0 means no chunking) between each
-     *        chunkSeparator (e.g. CRLF).
-     * @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF).
+     * <p>
+     * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once
+     * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1"
+     * call is not necessary when decoding, but it doesn't hurt, either.
+     * </p>
+     * <p>
+     * Ignores all non-Base32 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are
+     * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in,
+     * garbage-out philosophy: it will not check the provided data for validity.
+     * </p>
+     * 
+     * @param in
+     *            byte[] array of ascii data to Base32 decode.
+     * @param inPos
+     *            Position to start reading data from.
+     * @param inAvail
+     *            Amount of bytes available from input for encoding.
      *
-     * @return amount of space needed to encoded the supplied array.  Returns
-     *         a long since a max-len array will require Integer.MAX_VALUE + 33%.
+     * Output is written to {@link #buffer} as 8-bit octets, using {@link pos} as the buffer position
      */
-    private static long getEncodeLength(byte[] pArray, int chunkSize, byte[] chunkSeparator) {
-        // Base32 always encodes to multiples of 8 (BYTES_PER_ENCODED_CHUNK).
-        chunkSize = (chunkSize / BYTES_PER_ENCODED_BLOCK) * BYTES_PER_ENCODED_BLOCK;
-
-        long len = (pArray.length * BYTES_PER_ENCODED_BLOCK) / BYTES_PER_UNENCODED_BLOCK;
-        long mod = len % BYTES_PER_ENCODED_BLOCK;
-        if (mod != 0) {
-            len += BYTES_PER_ENCODED_BLOCK - mod;
-        }
-        if (chunkSize > 0) {
-            boolean lenChunksPerfectly = len % chunkSize == 0;
-            len += (len / chunkSize) * chunkSeparator.length;
-            if (!lenChunksPerfectly) {
-                len += chunkSeparator.length;
+    void decode(byte[] in, int inPos, int inAvail) { // package protected for access from I/O streams
+        if (eof) {
+            return;
+        }
+        if (inAvail < 0) {
+            eof = true;
+        }
+        for (int i = 0; i < inAvail; i++) {
+            if (buffer == null || buffer.length - pos < decodeSize) {
+                resizeBuffer();
+            }
+            byte b = in[inPos++];
+            if (b == PAD) {
+                // We're done.
+                eof = true;
+                break;
+            } else {
+                if (b >= 0 && b < this.decodeTable.length) {
+                    int result = this.decodeTable[b];
+                    if (result >= 0) {
+                        modulus = (++modulus) % BYTES_PER_ENCODED_BLOCK;
+                        x = (x << BITS_PER_ENCODED_CHAR) + result; // collect decoded bytes
+                        if (modulus == 0) { // we can output the 5 bytes
+                            buffer[pos++] = (byte) ((x >> 32) & MASK_8BITS);
+                            buffer[pos++] = (byte) ((x >> 24) & MASK_8BITS);
+                            buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
+                            buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
+                            buffer[pos++] = (byte) (x & MASK_8BITS);
+                        }
+                    }
+                }
+            }
+        }
+    
+        // Two forms of EOF as far as Base32 decoder is concerned: actual
+        // EOF (-1) and first time '=' character is encountered in stream.
+        // This approach makes the '=' padding characters completely optional.
+        if (eof && modulus != 0) {
+            if (buffer == null || buffer.length - pos < decodeSize) {
+                resizeBuffer();
+            }
+    
+            //  we ignore partial bytes, i.e. only multiples of 8 count
+            switch (modulus) {
+                case 2 : // 10 bits, drop 2 and output one byte
+                    buffer[pos++] = (byte) ((x >> 2) & MASK_8BITS);
+                    break;
+                case 3 : // 15 bits, drop 7 and output 1 byte
+                    buffer[pos++] = (byte) ((x >> 7) & MASK_8BITS);
+                    break;
+                case 4 : // 20 bits = 2*8 + 4
+                    x = x >> 4; // drop 4 bits
+                    buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
+                    buffer[pos++] = (byte) ((x) & MASK_8BITS);
+                    break;
+                case 5 : // 25bits = 3*8 + 1
+                    x = x >> 1;
+                    buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
+                    buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
+                    buffer[pos++] = (byte) ((x) & MASK_8BITS);
+                    break;
+                case 6 : // 30bits = 3*8 + 6
+                    x = x >> 6;
+                    buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
+                    buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
+                    buffer[pos++] = (byte) ((x) & MASK_8BITS);
+                    break;
+                case 7 : // 35 = 4*8 +3
+                    x = x >> 3;
+                    buffer[pos++] = (byte) ((x >> 24) & MASK_8BITS);
+                    buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
+                    buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
+                    buffer[pos++] = (byte) ((x) & MASK_8BITS);
+                    break;
             }
         }
-        return len;
     }
 
-    /**
-     * Resets this Base32 object to its initial newly constructed state.
-     */
-    private void reset() {
-        buffer = null;
-        pos = 0;
-        readPos = 0;
-        currentLinePos = 0;
-        modulus = 0;
-        eof = false;
-    }
+  /**
+  * Pre-calculates the amount of space needed to Base32-encode the supplied array.
+  *
+  * @param pArray byte[] array which will later be encoded
+  * @param chunkSize line-length of the output (<= 0 means no chunking) between each
+  *        chunkSeparator (e.g. CRLF).
+  * @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF).
+  *
+  * @return amount of space needed to encoded the supplied array.  Returns
+  *         a long since a max-len array will require Integer.MAX_VALUE + 33%.
+  */
+ private static long getEncodeLength(byte[] pArray, int chunkSize,
+         byte[] chunkSeparator) {
+             // Base32 always encodes to multiples of 8 (BYTES_PER_ENCODED_CHUNK).
+             chunkSize = (chunkSize / BYTES_PER_ENCODED_BLOCK) * BYTES_PER_ENCODED_BLOCK;
+         
+             long len = (pArray.length * BYTES_PER_ENCODED_BLOCK) / BYTES_PER_UNENCODED_BLOCK;
+             long mod = len % BYTES_PER_ENCODED_BLOCK;
+             if (mod != 0) {
+                 len += BYTES_PER_ENCODED_BLOCK - mod;
+             }
+             if (chunkSize > 0) {
+                 boolean lenChunksPerfectly = len % chunkSize == 0;
+                 len += (len / chunkSize) * chunkSeparator.length;
+                 if (!lenChunksPerfectly) {
+                     len += chunkSeparator.length;
+                 }
+             }
+             return len;
+         }
 
 }