You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2020/05/17 19:46:11 UTC

[commons-codec] branch master updated: Sort methods in AB order.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-codec.git


The following commit(s) were added to refs/heads/master by this push:
     new e09b236  Sort methods in AB order.
e09b236 is described below

commit e09b236f8850133dd9687d4a0e345e2ffbf79cd1
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun May 17 15:46:07 2020 -0400

    Sort methods in AB order.
---
 .../org/apache/commons/codec/binary/Base32.java    |   46 +-
 .../org/apache/commons/codec/binary/Base64.java    | 1016 ++++++++++----------
 .../apache/commons/codec/binary/BaseNCodec.java    |  554 +++++------
 .../codec/binary/BaseNCodecInputStream.java        |   28 +-
 .../codec/binary/BaseNCodecOutputStream.java       |  138 +--
 .../apache/commons/codec/binary/BinaryCodec.java   |  196 ++--
 .../java/org/apache/commons/codec/binary/Hex.java  |   94 +-
 .../apache/commons/codec/binary/StringUtils.java   |   32 +-
 8 files changed, 1052 insertions(+), 1052 deletions(-)

diff --git a/src/main/java/org/apache/commons/codec/binary/Base32.java b/src/main/java/org/apache/commons/codec/binary/Base32.java
index 8d57861..845206f 100644
--- a/src/main/java/org/apache/commons/codec/binary/Base32.java
+++ b/src/main/java/org/apache/commons/codec/binary/Base32.java
@@ -172,10 +172,10 @@ public class Base32 extends BaseNCodec {
      * <p>
      * When encoding the line length is 0 (no chunking).
      * </p>
-     * @param pad byte used as padding byte.
+     * @param useHex if {@code true} then use Base32 Hex alphabet
      */
-    public Base32(final byte pad) {
-        this(false, pad);
+    public Base32(final boolean useHex) {
+        this(0, null, useHex, PAD_DEFAULT);
     }
 
     /**
@@ -184,9 +184,10 @@ public class Base32 extends BaseNCodec {
      * When encoding the line length is 0 (no chunking).
      * </p>
      * @param useHex if {@code true} then use Base32 Hex alphabet
+     * @param padding byte used as padding byte.
      */
-    public Base32(final boolean useHex) {
-        this(0, null, useHex, PAD_DEFAULT);
+    public Base32(final boolean useHex, final byte padding) {
+        this(0, null, useHex, padding);
     }
 
     /**
@@ -194,11 +195,10 @@ public class Base32 extends BaseNCodec {
      * <p>
      * When encoding the line length is 0 (no chunking).
      * </p>
-     * @param useHex if {@code true} then use Base32 Hex alphabet
-     * @param padding byte used as padding byte.
+     * @param pad byte used as padding byte.
      */
-    public Base32(final boolean useHex, final byte padding) {
-        this(0, null, useHex, padding);
+    public Base32(final byte pad) {
+        this(false, pad);
     }
 
     /**
@@ -588,20 +588,6 @@ public class Base32 extends BaseNCodec {
     }
 
     /**
-     * Validates whether decoding allows final trailing characters that cannot be
-     * created during encoding.
-     *
-     * @throws IllegalArgumentException if strict decoding is enabled
-     */
-    private void validateTrailingCharacters() {
-        if (isStrictDecoding()) {
-            throw new IllegalArgumentException(
-                "Strict decoding: Last encoded character(s) (before the paddings if any) are valid base 32 alphabet but not a possible encoding. " +
-                "Decoding requries either 2, 4, 5, or 7 trailing 5-bit characters to create bytes.");
-        }
-    }
-
-    /**
      * Validates whether decoding the final trailing character is possible in the context
      * of the set of possible base 32 values.
      *
@@ -621,4 +607,18 @@ public class Base32 extends BaseNCodec {
                 "Expected the discarded bits from the character to be zero.");
         }
     }
+
+    /**
+     * Validates whether decoding allows final trailing characters that cannot be
+     * created during encoding.
+     *
+     * @throws IllegalArgumentException if strict decoding is enabled
+     */
+    private void validateTrailingCharacters() {
+        if (isStrictDecoding()) {
+            throw new IllegalArgumentException(
+                "Strict decoding: Last encoded character(s) (before the paddings if any) are valid base 32 alphabet but not a possible encoding. " +
+                "Decoding requries either 2, 4, 5, or 7 trailing 5-bit characters to create bytes.");
+        }
+    }
 }
diff --git a/src/main/java/org/apache/commons/codec/binary/Base64.java b/src/main/java/org/apache/commons/codec/binary/Base64.java
index 178633d..72cff5f 100644
--- a/src/main/java/org/apache/commons/codec/binary/Base64.java
+++ b/src/main/java/org/apache/commons/codec/binary/Base64.java
@@ -130,389 +130,194 @@ public class Base64 extends BaseNCodec {
     // some state be preserved between calls of encode() and decode().
 
     /**
-     * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able
-     * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch
-     * between the two modes.
-     */
-    private final byte[] encodeTable;
-
-    // Only one decode table currently; keep for consistency with Base32 code
-    private final byte[] decodeTable = DECODE_TABLE;
-
-    /**
-     * Line separator for encoding. Not used when decoding. Only used if lineLength &gt; 0.
-     */
-    private final byte[] lineSeparator;
-
-    /**
-     * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.
-     * {@code decodeSize = 3 + lineSeparator.length;}
-     */
-    private final int decodeSize;
-
-    /**
-     * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.
-     * {@code encodeSize = 4 + lineSeparator.length;}
-     */
-    private final int encodeSize;
-
-    /**
-     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
+     * Decodes Base64 data into octets.
      * <p>
-     * When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE.
+     * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.
      * </p>
      *
-     * <p>
-     * When decoding all variants are supported.
-     * </p>
+     * @param base64Data
+     *            Byte array containing Base64 data
+     * @return Array containing decoded data.
      */
-    public Base64() {
-        this(0);
+    public static byte[] decodeBase64(final byte[] base64Data) {
+        return new Base64().decode(base64Data);
     }
 
     /**
-     * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode.
-     * <p>
-     * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE.
-     * </p>
-     *
+     * Decodes a Base64 String into octets.
      * <p>
-     * When decoding all variants are supported.
+     * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.
      * </p>
      *
-     * @param urlSafe
-     *            if {@code true}, URL-safe encoding is used. In most cases this should be set to
-     *            {@code false}.
+     * @param base64String
+     *            String containing Base64 data
+     * @return Array containing decoded data.
      * @since 1.4
      */
-    public Base64(final boolean urlSafe) {
-        this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
+    public static byte[] decodeBase64(final String base64String) {
+        return new Base64().decode(base64String);
     }
 
+    // Implementation of integer encoding used for crypto
     /**
-     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
-     * <p>
-     * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is
-     * STANDARD_ENCODE_TABLE.
-     * </p>
-     * <p>
-     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
-     * </p>
-     * <p>
-     * When decoding all variants are supported.
-     * </p>
+     * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature.
      *
-     * @param lineLength
-     *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
-     *            4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when
-     *            decoding.
+     * @param pArray
+     *            a byte array containing base64 character data
+     * @return A BigInteger
      * @since 1.4
      */
-    public Base64(final int lineLength) {
-        this(lineLength, CHUNK_SEPARATOR);
+    public static BigInteger decodeInteger(final byte[] pArray) {
+        return new BigInteger(1, decodeBase64(pArray));
     }
 
     /**
-     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
-     * <p>
-     * When encoding the line length and line separator are given in the constructor, and the encoding table is
-     * STANDARD_ENCODE_TABLE.
-     * </p>
-     * <p>
-     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
-     * </p>
-     * <p>
-     * When decoding all variants are supported.
-     * </p>
+     * Encodes binary data using the base64 algorithm but does not chunk the output.
      *
-     * @param lineLength
-     *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
-     *            4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when
-     *            decoding.
-     * @param lineSeparator
-     *            Each line of encoded data will end with this sequence of bytes.
+     * @param binaryData
+     *            binary data to encode
+     * @return byte[] containing Base64 characters in their UTF-8 representation.
+     */
+    public static byte[] encodeBase64(final byte[] binaryData) {
+        return encodeBase64(binaryData, false);
+    }
+
+    /**
+     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
+     *
+     * @param binaryData
+     *            Array containing binary data to encode.
+     * @param isChunked
+     *            if {@code true} this encoder will chunk the base64 output into 76 character blocks
+     * @return Base64-encoded data.
      * @throws IllegalArgumentException
-     *             Thrown when the provided lineSeparator included some base64 characters.
-     * @since 1.4
+     *             Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
      */
-    public Base64(final int lineLength, final byte[] lineSeparator) {
-        this(lineLength, lineSeparator, false);
+    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) {
+        return encodeBase64(binaryData, isChunked, false);
     }
 
     /**
-     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
-     * <p>
-     * When encoding the line length and line separator are given in the constructor, and the encoding table is
-     * STANDARD_ENCODE_TABLE.
-     * </p>
-     * <p>
-     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
-     * </p>
-     * <p>
-     * When decoding all variants are supported.
-     * </p>
+     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
      *
-     * @param lineLength
-     *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
-     *            4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when
-     *            decoding.
-     * @param lineSeparator
-     *            Each line of encoded data will end with this sequence of bytes.
+     * @param binaryData
+     *            Array containing binary data to encode.
+     * @param isChunked
+     *            if {@code true} this encoder will chunk the base64 output into 76 character blocks
      * @param urlSafe
-     *            Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode
-     *            operations. Decoding seamlessly handles both modes.
-     *            <b>Note: no padding is added when using the URL-safe alphabet.</b>
+     *            if {@code true} this encoder will emit - and _ instead of the usual + and / characters.
+     *            <b>Note: no padding is added when encoding using the URL-safe alphabet.</b>
+     * @return Base64-encoded data.
      * @throws IllegalArgumentException
-     *             Thrown when the {@code lineSeparator} contains Base64 characters.
+     *             Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
      * @since 1.4
      */
-    public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) {
-        this(lineLength, lineSeparator, urlSafe, DECODING_POLICY_DEFAULT);
+    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) {
+        return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE);
     }
 
     /**
-     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
-     * <p>
-     * When encoding the line length and line separator are given in the constructor, and the encoding table is
-     * STANDARD_ENCODE_TABLE.
-     * </p>
-     * <p>
-     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
-     * </p>
-     * <p>
-     * When decoding all variants are supported.
-     * </p>
+     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
      *
-     * @param lineLength
-     *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
-     *            4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when
-     *            decoding.
-     * @param lineSeparator
-     *            Each line of encoded data will end with this sequence of bytes.
+     * @param binaryData
+     *            Array containing binary data to encode.
+     * @param isChunked
+     *            if {@code true} this encoder will chunk the base64 output into 76 character blocks
      * @param urlSafe
-     *            Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode
-     *            operations. Decoding seamlessly handles both modes.
-     *            <b>Note: no padding is added when using the URL-safe alphabet.</b>
-     * @param decodingPolicy The decoding policy.
+     *            if {@code true} this encoder will emit - and _ instead of the usual + and / characters.
+     *            <b>Note: no padding is added when encoding using the URL-safe alphabet.</b>
+     * @param maxResultSize
+     *            The maximum result size to accept.
+     * @return Base64-encoded data.
      * @throws IllegalArgumentException
-     *             Thrown when the {@code lineSeparator} contains Base64 characters.
-     * @since 1.15
+     *             Thrown when the input array needs an output array bigger than maxResultSize
+     * @since 1.4
      */
-    public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe, final CodecPolicy decodingPolicy) {
-        super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK,
-                lineLength,
-                lineSeparator == null ? 0 : lineSeparator.length,
-                PAD_DEFAULT,
-                decodingPolicy);
-        // TODO could be simplified if there is no requirement to reject invalid line sep when length <=0
-        // @see test case Base64Test.testConstructors()
-        if (lineSeparator != null) {
-            if (containsAlphabetOrPad(lineSeparator)) {
-                final String sep = StringUtils.newStringUtf8(lineSeparator);
-                throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]");
-            }
-            if (lineLength > 0){ // null line-sep forces no chunking rather than throwing IAE
-                this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length;
-                this.lineSeparator = new byte[lineSeparator.length];
-                System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
-            } else {
-                this.encodeSize = BYTES_PER_ENCODED_BLOCK;
-                this.lineSeparator = null;
-            }
-        } else {
-            this.encodeSize = BYTES_PER_ENCODED_BLOCK;
-            this.lineSeparator = null;
+    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked,
+                                      final boolean urlSafe, final int maxResultSize) {
+        if (binaryData == null || binaryData.length == 0) {
+            return binaryData;
         }
-        this.decodeSize = this.encodeSize - 1;
-        this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;
+
+        // Create this so can use the super-class method
+        // Also ensures that the same roundings are performed by the ctor and the code
+        final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe);
+        final long len = b64.getEncodedLength(binaryData);
+        if (len > maxResultSize) {
+            throw new IllegalArgumentException("Input array too big, the output array would be bigger (" +
+                len +
+                ") than the specified maximum size of " +
+                maxResultSize);
+        }
+
+        return b64.encode(binaryData);
     }
 
     /**
-     * Returns our current encode mode. True if we're URL-SAFE, false otherwise.
+     * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
      *
-     * @return true if we're in URL-SAFE mode, false otherwise.
-     * @since 1.4
+     * @param binaryData
+     *            binary data to encode
+     * @return Base64 characters chunked in 76 character blocks
      */
-    public boolean isUrlSafe() {
-        return this.encodeTable == URL_SAFE_ENCODE_TABLE;
+    public static byte[] encodeBase64Chunked(final byte[] binaryData) {
+        return encodeBase64(binaryData, true);
     }
 
     /**
-     * <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, to flush last
-     * remaining bytes (if not multiple of 3).
-     * </p>
-     * <p><b>Note: no padding is added when encoding using the URL-safe alphabet.</b></p>
-     * <p>
-     * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
-     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
-     * </p>
+     * Encodes binary data using the base64 algorithm but does not chunk the output.
      *
-     * @param in
-     *            byte[] array of binary data to base64 encode.
-     * @param inPos
-     *            Position to start reading data from.
-     * @param inAvail
-     *            Amount of bytes available from input for encoding.
-     * @param context
-     *            the context to be used
+     * NOTE:  We changed the behavior of this method from multi-line chunking (commons-codec-1.4) to
+     * single-line non-chunking (commons-codec-1.5).
+     *
+     * @param binaryData
+     *            binary data to encode
+     * @return String containing Base64 characters.
+     * @since 1.4 (NOTE:  1.4 chunked the output, whereas 1.5 does not).
      */
-    @Override
-    void encode(final byte[] in, int inPos, final int inAvail, final Context context) {
-        if (context.eof) {
-            return;
-        }
-        // inAvail < 0 is how we're informed of EOF in the underlying data we're
-        // encoding.
-        if (inAvail < 0) {
-            context.eof = true;
-            if (0 == context.modulus && lineLength == 0) {
-                return; // no leftovers to process and not using chunking
-            }
-            final byte[] buffer = ensureBufferSize(encodeSize, context);
-            final int savedPos = context.pos;
-            switch (context.modulus) { // 0-2
-                case 0 : // nothing to do here
-                    break;
-                case 1 : // 8 bits = 6 + 2
-                    // top 6 bits:
-                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 2) & MASK_6BITS];
-                    // remaining 2:
-                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 4) & MASK_6BITS];
-                    // URL-SAFE skips the padding to further reduce size.
-                    if (encodeTable == STANDARD_ENCODE_TABLE) {
-                        buffer[context.pos++] = pad;
-                        buffer[context.pos++] = pad;
-                    }
-                    break;
-
-                case 2 : // 16 bits = 6 + 6 + 4
-                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 10) & MASK_6BITS];
-                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 4) & MASK_6BITS];
-                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 2) & MASK_6BITS];
-                    // URL-SAFE skips the padding to further reduce size.
-                    if (encodeTable == STANDARD_ENCODE_TABLE) {
-                        buffer[context.pos++] = pad;
-                    }
-                    break;
-                default:
-                    throw new IllegalStateException("Impossible modulus " + context.modulus);
-            }
-            context.currentLinePos += context.pos - savedPos; // keep track of current line position
-            // if currentPos == 0 we are at the start of a line, so don't add CRLF
-            if (lineLength > 0 && context.currentLinePos > 0) {
-                System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length);
-                context.pos += lineSeparator.length;
-            }
-        } else {
-            for (int i = 0; i < inAvail; i++) {
-                final byte[] buffer = ensureBufferSize(encodeSize, context);
-                context.modulus = (context.modulus+1) % BYTES_PER_UNENCODED_BLOCK;
-                int b = in[inPos++];
-                if (b < 0) {
-                    b += 256;
-                }
-                context.ibitWorkArea = (context.ibitWorkArea << 8) + b; //  BITS_PER_BYTE
-                if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract
-                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 18) & MASK_6BITS];
-                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 12) & MASK_6BITS];
-                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 6) & MASK_6BITS];
-                    buffer[context.pos++] = encodeTable[context.ibitWorkArea & MASK_6BITS];
-                    context.currentLinePos += BYTES_PER_ENCODED_BLOCK;
-                    if (lineLength > 0 && lineLength <= context.currentLinePos) {
-                        System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length);
-                        context.pos += lineSeparator.length;
-                        context.currentLinePos = 0;
-                    }
-                }
-            }
-        }
+    public static String encodeBase64String(final byte[] binaryData) {
+        return StringUtils.newStringUsAscii(encodeBase64(binaryData, false));
     }
 
     /**
-     * <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-base64 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>
-     * <p>
-     * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
-     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
-     * </p>
-     *
-     * @param in
-     *            byte[] array of ascii data to base64 decode.
-     * @param inPos
-     *            Position to start reading data from.
-     * @param inAvail
-     *            Amount of bytes available from input for decoding.
-     * @param context
-     *            the context to be used
+     * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The
+     * url-safe variation emits - and _ instead of + and / characters.
+     * <b>Note: no padding is added.</b>
+     * @param binaryData
+     *            binary data to encode
+     * @return byte[] containing Base64 characters in their UTF-8 representation.
+     * @since 1.4
      */
-    @Override
-    void decode(final byte[] in, int inPos, final int inAvail, final Context context) {
-        if (context.eof) {
-            return;
-        }
-        if (inAvail < 0) {
-            context.eof = true;
-        }
-        for (int i = 0; i < inAvail; i++) {
-            final byte[] buffer = ensureBufferSize(decodeSize, context);
-            final byte b = in[inPos++];
-            if (b == pad) {
-                // We're done.
-                context.eof = true;
-                break;
-            }
-            if (b >= 0 && b < DECODE_TABLE.length) {
-                final int result = DECODE_TABLE[b];
-                if (result >= 0) {
-                    context.modulus = (context.modulus+1) % BYTES_PER_ENCODED_BLOCK;
-                    context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result;
-                    if (context.modulus == 0) {
-                        buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS);
-                        buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
-                        buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);
-                    }
-                }
-            }
-        }
+    public static byte[] encodeBase64URLSafe(final byte[] binaryData) {
+        return encodeBase64(binaryData, false, true);
+    }
 
-        // Two forms of EOF as far as base64 decoder is concerned: actual
-        // EOF (-1) and first time '=' character is encountered in stream.
-        // This approach makes the '=' padding characters completely optional.
-        if (context.eof && context.modulus != 0) {
-            final byte[] buffer = ensureBufferSize(decodeSize, context);
+    /**
+     * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The
+     * url-safe variation emits - and _ instead of + and / characters.
+     * <b>Note: no padding is added.</b>
+     * @param binaryData
+     *            binary data to encode
+     * @return String containing Base64 characters
+     * @since 1.4
+     */
+    public static String encodeBase64URLSafeString(final byte[] binaryData) {
+        return StringUtils.newStringUsAscii(encodeBase64(binaryData, false, true));
+    }
 
-            // We have some spare bits remaining
-            // Output all whole multiples of 8 bits and ignore the rest
-            switch (context.modulus) {
-//              case 0 : // impossible, as excluded above
-                case 1 : // 6 bits - either ignore entirely, or raise an exception
-                    validateTrailingCharacter();
-                    break;
-                case 2 : // 12 bits = 8 + 4
-                    validateCharacter(MASK_4BITS, context);
-                    context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits
-                    buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
-                    break;
-                case 3 : // 18 bits = 8 + 8 + 2
-                    validateCharacter(MASK_2BITS, context);
-                    context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits
-                    buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
-                    buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
-                    break;
-                default:
-                    throw new IllegalStateException("Impossible modulus " + context.modulus);
-            }
-        }
+    /**
+     * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature.
+     *
+     * @param bigInteger
+     *            a BigInteger
+     * @return A byte array containing base64 character data
+     * @throws NullPointerException
+     *             if null is passed in
+     * @since 1.4
+     */
+    public static byte[] encodeInteger(final BigInteger bigInteger) {
+        Objects.requireNonNull(bigInteger, "bigInteger");
+        return encodeBase64(toIntegerBytes(bigInteger), false);
     }
 
     /**
@@ -543,20 +348,6 @@ public class Base64 extends BaseNCodec {
     }
 
     /**
-     * Tests a given String to see if it contains only valid characters within the Base64 alphabet. Currently the
-     * method treats whitespace as valid.
-     *
-     * @param base64
-     *            String to test
-     * @return {@code true} if all characters in the String are valid characters in the Base64 alphabet or if
-     *         the String is empty; {@code false}, otherwise
-     *  @since 1.5
-     */
-    public static boolean isBase64(final String base64) {
-        return isBase64(StringUtils.getBytesUtf8(base64));
-    }
-
-    /**
      * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the
      * method treats whitespace as valid.
      *
@@ -576,227 +367,426 @@ public class Base64 extends BaseNCodec {
     }
 
     /**
-     * Encodes binary data using the base64 algorithm but does not chunk the output.
+     * Tests a given String to see if it contains only valid characters within the Base64 alphabet. Currently the
+     * method treats whitespace as valid.
      *
-     * @param binaryData
-     *            binary data to encode
-     * @return byte[] containing Base64 characters in their UTF-8 representation.
+     * @param base64
+     *            String to test
+     * @return {@code true} if all characters in the String are valid characters in the Base64 alphabet or if
+     *         the String is empty; {@code false}, otherwise
+     *  @since 1.5
      */
-    public static byte[] encodeBase64(final byte[] binaryData) {
-        return encodeBase64(binaryData, false);
+    public static boolean isBase64(final String base64) {
+        return isBase64(StringUtils.getBytesUtf8(base64));
     }
 
     /**
-     * Encodes binary data using the base64 algorithm but does not chunk the output.
-     *
-     * NOTE:  We changed the behavior of this method from multi-line chunking (commons-codec-1.4) to
-     * single-line non-chunking (commons-codec-1.5).
+     * Returns a byte-array representation of a {@code BigInteger} without sign bit.
      *
-     * @param binaryData
-     *            binary data to encode
-     * @return String containing Base64 characters.
-     * @since 1.4 (NOTE:  1.4 chunked the output, whereas 1.5 does not).
+     * @param bigInt
+     *            {@code BigInteger} to be converted
+     * @return a byte array representation of the BigInteger parameter
      */
-    public static String encodeBase64String(final byte[] binaryData) {
-        return StringUtils.newStringUsAscii(encodeBase64(binaryData, false));
+    static byte[] toIntegerBytes(final BigInteger bigInt) {
+        int bitlen = bigInt.bitLength();
+        // round bitlen
+        bitlen = ((bitlen + 7) >> 3) << 3;
+        final byte[] bigBytes = bigInt.toByteArray();
+
+        if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
+            return bigBytes;
+        }
+        // set up params for copying everything but sign bit
+        int startSrc = 0;
+        int len = bigBytes.length;
+
+        // if bigInt is exactly byte-aligned, just skip signbit in copy
+        if ((bigInt.bitLength() % 8) == 0) {
+            startSrc = 1;
+            len--;
+        }
+        final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
+        final byte[] resizedBytes = new byte[bitlen / 8];
+        System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
+        return resizedBytes;
     }
 
     /**
-     * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The
-     * url-safe variation emits - and _ instead of + and / characters.
-     * <b>Note: no padding is added.</b>
-     * @param binaryData
-     *            binary data to encode
-     * @return byte[] containing Base64 characters in their UTF-8 representation.
-     * @since 1.4
+     * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able
+     * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch
+     * between the two modes.
      */
-    public static byte[] encodeBase64URLSafe(final byte[] binaryData) {
-        return encodeBase64(binaryData, false, true);
-    }
+    private final byte[] encodeTable;
+
+    // Only one decode table currently; keep for consistency with Base32 code
+    private final byte[] decodeTable = DECODE_TABLE;
 
     /**
-     * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The
-     * url-safe variation emits - and _ instead of + and / characters.
-     * <b>Note: no padding is added.</b>
-     * @param binaryData
-     *            binary data to encode
-     * @return String containing Base64 characters
-     * @since 1.4
+     * Line separator for encoding. Not used when decoding. Only used if lineLength &gt; 0.
      */
-    public static String encodeBase64URLSafeString(final byte[] binaryData) {
-        return StringUtils.newStringUsAscii(encodeBase64(binaryData, false, true));
+    private final byte[] lineSeparator;
+
+    /**
+     * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.
+     * {@code decodeSize = 3 + lineSeparator.length;}
+     */
+    private final int decodeSize;
+
+    /**
+     * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.
+     * {@code encodeSize = 4 + lineSeparator.length;}
+     */
+    private final int encodeSize;
+
+    /**
+     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
+     * <p>
+     * When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE.
+     * </p>
+     *
+     * <p>
+     * When decoding all variants are supported.
+     * </p>
+     */
+    public Base64() {
+        this(0);
     }
 
     /**
-     * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
+     * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode.
+     * <p>
+     * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE.
+     * </p>
      *
-     * @param binaryData
-     *            binary data to encode
-     * @return Base64 characters chunked in 76 character blocks
+     * <p>
+     * When decoding all variants are supported.
+     * </p>
+     *
+     * @param urlSafe
+     *            if {@code true}, URL-safe encoding is used. In most cases this should be set to
+     *            {@code false}.
+     * @since 1.4
      */
-    public static byte[] encodeBase64Chunked(final byte[] binaryData) {
-        return encodeBase64(binaryData, true);
+    public Base64(final boolean urlSafe) {
+        this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
     }
 
     /**
-     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
+     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
+     * <p>
+     * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is
+     * STANDARD_ENCODE_TABLE.
+     * </p>
+     * <p>
+     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
+     * </p>
+     * <p>
+     * When decoding all variants are supported.
+     * </p>
      *
-     * @param binaryData
-     *            Array containing binary data to encode.
-     * @param isChunked
-     *            if {@code true} this encoder will chunk the base64 output into 76 character blocks
-     * @return Base64-encoded data.
-     * @throws IllegalArgumentException
-     *             Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
+     * @param lineLength
+     *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
+     *            4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when
+     *            decoding.
+     * @since 1.4
      */
-    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) {
-        return encodeBase64(binaryData, isChunked, false);
+    public Base64(final int lineLength) {
+        this(lineLength, CHUNK_SEPARATOR);
     }
 
     /**
-     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
+     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
+     * <p>
+     * When encoding the line length and line separator are given in the constructor, and the encoding table is
+     * STANDARD_ENCODE_TABLE.
+     * </p>
+     * <p>
+     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
+     * </p>
+     * <p>
+     * When decoding all variants are supported.
+     * </p>
      *
-     * @param binaryData
-     *            Array containing binary data to encode.
-     * @param isChunked
-     *            if {@code true} this encoder will chunk the base64 output into 76 character blocks
-     * @param urlSafe
-     *            if {@code true} this encoder will emit - and _ instead of the usual + and / characters.
-     *            <b>Note: no padding is added when encoding using the URL-safe alphabet.</b>
-     * @return Base64-encoded data.
+     * @param lineLength
+     *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
+     *            4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when
+     *            decoding.
+     * @param lineSeparator
+     *            Each line of encoded data will end with this sequence of bytes.
      * @throws IllegalArgumentException
-     *             Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
+     *             Thrown when the provided lineSeparator included some base64 characters.
      * @since 1.4
      */
-    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) {
-        return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE);
+    public Base64(final int lineLength, final byte[] lineSeparator) {
+        this(lineLength, lineSeparator, false);
     }
 
     /**
-     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
+     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
+     * <p>
+     * When encoding the line length and line separator are given in the constructor, and the encoding table is
+     * STANDARD_ENCODE_TABLE.
+     * </p>
+     * <p>
+     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
+     * </p>
+     * <p>
+     * When decoding all variants are supported.
+     * </p>
      *
-     * @param binaryData
-     *            Array containing binary data to encode.
-     * @param isChunked
-     *            if {@code true} this encoder will chunk the base64 output into 76 character blocks
+     * @param lineLength
+     *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
+     *            4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when
+     *            decoding.
+     * @param lineSeparator
+     *            Each line of encoded data will end with this sequence of bytes.
      * @param urlSafe
-     *            if {@code true} this encoder will emit - and _ instead of the usual + and / characters.
-     *            <b>Note: no padding is added when encoding using the URL-safe alphabet.</b>
-     * @param maxResultSize
-     *            The maximum result size to accept.
-     * @return Base64-encoded data.
+     *            Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode
+     *            operations. Decoding seamlessly handles both modes.
+     *            <b>Note: no padding is added when using the URL-safe alphabet.</b>
      * @throws IllegalArgumentException
-     *             Thrown when the input array needs an output array bigger than maxResultSize
+     *             Thrown when the {@code lineSeparator} contains Base64 characters.
      * @since 1.4
      */
-    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked,
-                                      final boolean urlSafe, final int maxResultSize) {
-        if (binaryData == null || binaryData.length == 0) {
-            return binaryData;
-        }
-
-        // Create this so can use the super-class method
-        // Also ensures that the same roundings are performed by the ctor and the code
-        final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe);
-        final long len = b64.getEncodedLength(binaryData);
-        if (len > maxResultSize) {
-            throw new IllegalArgumentException("Input array too big, the output array would be bigger (" +
-                len +
-                ") than the specified maximum size of " +
-                maxResultSize);
-        }
-
-        return b64.encode(binaryData);
+    public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) {
+        this(lineLength, lineSeparator, urlSafe, DECODING_POLICY_DEFAULT);
     }
 
     /**
-     * Decodes a Base64 String into octets.
+     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
      * <p>
-     * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.
+     * When encoding the line length and line separator are given in the constructor, and the encoding table is
+     * STANDARD_ENCODE_TABLE.
+     * </p>
+     * <p>
+     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
+     * </p>
+     * <p>
+     * When decoding all variants are supported.
      * </p>
      *
-     * @param base64String
-     *            String containing Base64 data
-     * @return Array containing decoded data.
-     * @since 1.4
+     * @param lineLength
+     *            Each line of encoded data will be at most of the given length (rounded down to nearest multiple of
+     *            4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when
+     *            decoding.
+     * @param lineSeparator
+     *            Each line of encoded data will end with this sequence of bytes.
+     * @param urlSafe
+     *            Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode
+     *            operations. Decoding seamlessly handles both modes.
+     *            <b>Note: no padding is added when using the URL-safe alphabet.</b>
+     * @param decodingPolicy The decoding policy.
+     * @throws IllegalArgumentException
+     *             Thrown when the {@code lineSeparator} contains Base64 characters.
+     * @since 1.15
      */
-    public static byte[] decodeBase64(final String base64String) {
-        return new Base64().decode(base64String);
+    public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe, final CodecPolicy decodingPolicy) {
+        super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK,
+                lineLength,
+                lineSeparator == null ? 0 : lineSeparator.length,
+                PAD_DEFAULT,
+                decodingPolicy);
+        // TODO could be simplified if there is no requirement to reject invalid line sep when length <=0
+        // @see test case Base64Test.testConstructors()
+        if (lineSeparator != null) {
+            if (containsAlphabetOrPad(lineSeparator)) {
+                final String sep = StringUtils.newStringUtf8(lineSeparator);
+                throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]");
+            }
+            if (lineLength > 0){ // null line-sep forces no chunking rather than throwing IAE
+                this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length;
+                this.lineSeparator = new byte[lineSeparator.length];
+                System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
+            } else {
+                this.encodeSize = BYTES_PER_ENCODED_BLOCK;
+                this.lineSeparator = null;
+            }
+        } else {
+            this.encodeSize = BYTES_PER_ENCODED_BLOCK;
+            this.lineSeparator = null;
+        }
+        this.decodeSize = this.encodeSize - 1;
+        this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;
     }
 
+    // Implementation of the Encoder Interface
+
     /**
-     * Decodes Base64 data into octets.
      * <p>
-     * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.
+     * 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-base64 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>
+     * <p>
+     * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
+     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
      * </p>
      *
-     * @param base64Data
-     *            Byte array containing Base64 data
-     * @return Array containing decoded data.
+     * @param in
+     *            byte[] array of ascii data to base64 decode.
+     * @param inPos
+     *            Position to start reading data from.
+     * @param inAvail
+     *            Amount of bytes available from input for decoding.
+     * @param context
+     *            the context to be used
      */
-    public static byte[] decodeBase64(final byte[] base64Data) {
-        return new Base64().decode(base64Data);
-    }
-
-    // Implementation of the Encoder Interface
+    @Override
+    void decode(final byte[] in, int inPos, final int inAvail, final Context context) {
+        if (context.eof) {
+            return;
+        }
+        if (inAvail < 0) {
+            context.eof = true;
+        }
+        for (int i = 0; i < inAvail; i++) {
+            final byte[] buffer = ensureBufferSize(decodeSize, context);
+            final byte b = in[inPos++];
+            if (b == pad) {
+                // We're done.
+                context.eof = true;
+                break;
+            }
+            if (b >= 0 && b < DECODE_TABLE.length) {
+                final int result = DECODE_TABLE[b];
+                if (result >= 0) {
+                    context.modulus = (context.modulus+1) % BYTES_PER_ENCODED_BLOCK;
+                    context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result;
+                    if (context.modulus == 0) {
+                        buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS);
+                        buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
+                        buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);
+                    }
+                }
+            }
+        }
 
-    // Implementation of integer encoding used for crypto
-    /**
-     * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature.
-     *
-     * @param pArray
-     *            a byte array containing base64 character data
-     * @return A BigInteger
-     * @since 1.4
-     */
-    public static BigInteger decodeInteger(final byte[] pArray) {
-        return new BigInteger(1, decodeBase64(pArray));
-    }
+        // Two forms of EOF as far as base64 decoder is concerned: actual
+        // EOF (-1) and first time '=' character is encountered in stream.
+        // This approach makes the '=' padding characters completely optional.
+        if (context.eof && context.modulus != 0) {
+            final byte[] buffer = ensureBufferSize(decodeSize, context);
 
-    /**
-     * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature.
-     *
-     * @param bigInteger
-     *            a BigInteger
-     * @return A byte array containing base64 character data
-     * @throws NullPointerException
-     *             if null is passed in
-     * @since 1.4
-     */
-    public static byte[] encodeInteger(final BigInteger bigInteger) {
-        Objects.requireNonNull(bigInteger, "bigInteger");
-        return encodeBase64(toIntegerBytes(bigInteger), false);
+            // We have some spare bits remaining
+            // Output all whole multiples of 8 bits and ignore the rest
+            switch (context.modulus) {
+//              case 0 : // impossible, as excluded above
+                case 1 : // 6 bits - either ignore entirely, or raise an exception
+                    validateTrailingCharacter();
+                    break;
+                case 2 : // 12 bits = 8 + 4
+                    validateCharacter(MASK_4BITS, context);
+                    context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits
+                    buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
+                    break;
+                case 3 : // 18 bits = 8 + 8 + 2
+                    validateCharacter(MASK_2BITS, context);
+                    context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits
+                    buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
+                    buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
+                    break;
+                default:
+                    throw new IllegalStateException("Impossible modulus " + context.modulus);
+            }
+        }
     }
 
     /**
-     * Returns a byte-array representation of a {@code BigInteger} without sign bit.
+     * <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, to flush last
+     * remaining bytes (if not multiple of 3).
+     * </p>
+     * <p><b>Note: no padding is added when encoding using the URL-safe alphabet.</b></p>
+     * <p>
+     * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
+     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
+     * </p>
      *
-     * @param bigInt
-     *            {@code BigInteger} to be converted
-     * @return a byte array representation of the BigInteger parameter
+     * @param in
+     *            byte[] array of binary data to base64 encode.
+     * @param inPos
+     *            Position to start reading data from.
+     * @param inAvail
+     *            Amount of bytes available from input for encoding.
+     * @param context
+     *            the context to be used
      */
-    static byte[] toIntegerBytes(final BigInteger bigInt) {
-        int bitlen = bigInt.bitLength();
-        // round bitlen
-        bitlen = ((bitlen + 7) >> 3) << 3;
-        final byte[] bigBytes = bigInt.toByteArray();
-
-        if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
-            return bigBytes;
+    @Override
+    void encode(final byte[] in, int inPos, final int inAvail, final Context context) {
+        if (context.eof) {
+            return;
         }
-        // set up params for copying everything but sign bit
-        int startSrc = 0;
-        int len = bigBytes.length;
+        // inAvail < 0 is how we're informed of EOF in the underlying data we're
+        // encoding.
+        if (inAvail < 0) {
+            context.eof = true;
+            if (0 == context.modulus && lineLength == 0) {
+                return; // no leftovers to process and not using chunking
+            }
+            final byte[] buffer = ensureBufferSize(encodeSize, context);
+            final int savedPos = context.pos;
+            switch (context.modulus) { // 0-2
+                case 0 : // nothing to do here
+                    break;
+                case 1 : // 8 bits = 6 + 2
+                    // top 6 bits:
+                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 2) & MASK_6BITS];
+                    // remaining 2:
+                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 4) & MASK_6BITS];
+                    // URL-SAFE skips the padding to further reduce size.
+                    if (encodeTable == STANDARD_ENCODE_TABLE) {
+                        buffer[context.pos++] = pad;
+                        buffer[context.pos++] = pad;
+                    }
+                    break;
 
-        // if bigInt is exactly byte-aligned, just skip signbit in copy
-        if ((bigInt.bitLength() % 8) == 0) {
-            startSrc = 1;
-            len--;
+                case 2 : // 16 bits = 6 + 6 + 4
+                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 10) & MASK_6BITS];
+                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 4) & MASK_6BITS];
+                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea << 2) & MASK_6BITS];
+                    // URL-SAFE skips the padding to further reduce size.
+                    if (encodeTable == STANDARD_ENCODE_TABLE) {
+                        buffer[context.pos++] = pad;
+                    }
+                    break;
+                default:
+                    throw new IllegalStateException("Impossible modulus " + context.modulus);
+            }
+            context.currentLinePos += context.pos - savedPos; // keep track of current line position
+            // if currentPos == 0 we are at the start of a line, so don't add CRLF
+            if (lineLength > 0 && context.currentLinePos > 0) {
+                System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length);
+                context.pos += lineSeparator.length;
+            }
+        } else {
+            for (int i = 0; i < inAvail; i++) {
+                final byte[] buffer = ensureBufferSize(encodeSize, context);
+                context.modulus = (context.modulus+1) % BYTES_PER_UNENCODED_BLOCK;
+                int b = in[inPos++];
+                if (b < 0) {
+                    b += 256;
+                }
+                context.ibitWorkArea = (context.ibitWorkArea << 8) + b; //  BITS_PER_BYTE
+                if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract
+                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 18) & MASK_6BITS];
+                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 12) & MASK_6BITS];
+                    buffer[context.pos++] = encodeTable[(context.ibitWorkArea >> 6) & MASK_6BITS];
+                    buffer[context.pos++] = encodeTable[context.ibitWorkArea & MASK_6BITS];
+                    context.currentLinePos += BYTES_PER_ENCODED_BLOCK;
+                    if (lineLength > 0 && lineLength <= context.currentLinePos) {
+                        System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length);
+                        context.pos += lineSeparator.length;
+                        context.currentLinePos = 0;
+                    }
+                }
+            }
         }
-        final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
-        final byte[] resizedBytes = new byte[bitlen / 8];
-        System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
-        return resizedBytes;
     }
 
     /**
@@ -812,17 +802,13 @@ public class Base64 extends BaseNCodec {
     }
 
     /**
-     * Validates whether decoding allows an entire final trailing character that cannot be
-     * used for a complete byte.
+     * Returns our current encode mode. True if we're URL-SAFE, false otherwise.
      *
-     * @throws IllegalArgumentException if strict decoding is enabled
+     * @return true if we're in URL-SAFE mode, false otherwise.
+     * @since 1.4
      */
-    private void validateTrailingCharacter() {
-        if (isStrictDecoding()) {
-            throw new IllegalArgumentException(
-                "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " +
-                "Decoding requries at least two trailing 6-bit characters to create bytes.");
-        }
+    public boolean isUrlSafe() {
+        return this.encodeTable == URL_SAFE_ENCODE_TABLE;
     }
 
     /**
@@ -845,4 +831,18 @@ public class Base64 extends BaseNCodec {
         }
     }
 
+    /**
+     * Validates whether decoding allows an entire final trailing character that cannot be
+     * used for a complete byte.
+     *
+     * @throws IllegalArgumentException if strict decoding is enabled
+     */
+    private void validateTrailingCharacter() {
+        if (isStrictDecoding()) {
+            throw new IllegalArgumentException(
+                "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " +
+                "Decoding requries at least two trailing 6-bit characters to create bytes.");
+        }
+    }
+
 }
diff --git a/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java b/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java
index b45437d..1fe339c 100644
--- a/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java
+++ b/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java
@@ -193,6 +193,104 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
     static final byte[] CHUNK_SEPARATOR = {'\r', '\n'};
 
     /**
+     * Compares two {@code int} values numerically treating the values
+     * as unsigned. Taken from JDK 1.8.
+     *
+     * <p>TODO: Replace with JDK 1.8 Integer::compareUnsigned(int, int).</p>
+     *
+     * @param  x the first {@code int} to compare
+     * @param  y the second {@code int} to compare
+     * @return the value {@code 0} if {@code x == y}; a value less
+     *         than {@code 0} if {@code x < y} as unsigned values; and
+     *         a value greater than {@code 0} if {@code x > y} as
+     *         unsigned values
+     */
+    private static int compareUnsigned(final int x, final int y) {
+        return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
+    }
+
+    /**
+     * Create a positive capacity at least as large the minimum required capacity.
+     * If the minimum capacity is negative then this throws an OutOfMemoryError as no array
+     * can be allocated.
+     *
+     * @param minCapacity the minimum capacity
+     * @return the capacity
+     * @throws OutOfMemoryError if the {@code minCapacity} is negative
+     */
+    private static int createPositiveCapacity(final int minCapacity) {
+        if (minCapacity < 0) {
+            // overflow
+            throw new OutOfMemoryError("Unable to allocate array size: " + (minCapacity & 0xffffffffL));
+        }
+        // This is called when we require buffer expansion to a very big array.
+        // Use the conservative maximum buffer size if possible, otherwise the biggest required.
+        //
+        // Note: In this situation JDK 1.8 java.util.ArrayList returns Integer.MAX_VALUE.
+        // This excludes some VMs that can exceed MAX_BUFFER_SIZE but not allocate a full
+        // Integer.MAX_VALUE length array.
+        // The result is that we may have to allocate an array of this size more than once if
+        // the capacity must be expanded again.
+        return (minCapacity > MAX_BUFFER_SIZE) ?
+            minCapacity :
+            MAX_BUFFER_SIZE;
+    }
+
+    /**
+     * Gets a copy of the chunk separator per RFC 2045 section 2.1.
+     *
+     * @return the chunk separator
+     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
+     * @since 1.15
+     */
+    public static byte[] getChunkSeparator() {
+        return CHUNK_SEPARATOR.clone();
+    }
+
+    /**
+     * Checks if a byte value is whitespace or not.
+     * Whitespace is taken to mean: space, tab, CR, LF
+     * @param byteToCheck
+     *            the byte to check
+     * @return true if byte is whitespace, false otherwise
+     */
+    protected static boolean isWhiteSpace(final byte byteToCheck) {
+        switch (byteToCheck) {
+            case ' ' :
+            case '\n' :
+            case '\r' :
+            case '\t' :
+                return true;
+            default :
+                return false;
+        }
+    }
+
+    /**
+     * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}.
+     * @param context the context to be used
+     * @param minCapacity the minimum required capacity
+     * @return the resized byte[] buffer
+     * @throws OutOfMemoryError if the {@code minCapacity} is negative
+     */
+    private static byte[] resizeBuffer(final Context context, final int minCapacity) {
+        // Overflow-conscious code treats the min and new capacity as unsigned.
+        final int oldCapacity = context.buffer.length;
+        int newCapacity = oldCapacity * DEFAULT_BUFFER_RESIZE_FACTOR;
+        if (compareUnsigned(newCapacity, minCapacity) < 0) {
+            newCapacity = minCapacity;
+        }
+        if (compareUnsigned(newCapacity, MAX_BUFFER_SIZE) > 0) {
+            newCapacity = createPositiveCapacity(minCapacity);
+        }
+
+        final byte[] b = new byte[newCapacity];
+        System.arraycopy(context.buffer, 0, b, 0, context.buffer.length);
+        context.buffer = b;
+        return b;
+    }
+
+    /**
      * @deprecated Use {@link #pad}. Will be removed in 2.0.
      */
     @Deprecated
@@ -288,47 +386,6 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
     }
 
     /**
-     * Returns true if decoding behavior is strict. Decoding will raise an {@link IllegalArgumentException} if trailing
-     * bits are not part of a valid encoding.
-     *
-     * <p>
-     * The default is false for lenient encoding. Decoding will compose trailing bits into 8-bit bytes and discard the
-     * remainder.
-     * </p>
-     *
-     * @return true if using strict decoding
-     * @since 1.15
-     */
-    public boolean isStrictDecoding() {
-        return decodingPolicy == CodecPolicy.STRICT;
-    }
-
-    /**
-     * Returns the decoding behavior policy. Decoding will raise an {@link IllegalArgumentException} if trailing bits
-     * are not part of a valid encoding.
-     *
-     * <p>
-     * The default is lenient. Decoding will compose trailing bits into 8-bit bytes and discard the remainder.
-     * </p>
-     *
-     * @return true if using strict decoding
-     * @since 1.15
-     */
-    public CodecPolicy getCodecPolicy() {
-        return decodingPolicy;
-    }
-
-    /**
-     * Returns true if this object has buffered data for reading.
-     *
-     * @param context the context to be used
-     * @return true if there is data still available for reading.
-     */
-    boolean hasData(final Context context) {  // package protected for access from I/O streams
-        return context.buffer != null;
-    }
-
-    /**
      * Returns the amount of buffered data available for reading.
      *
      * @param context the context to be used
@@ -339,204 +396,48 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
     }
 
     /**
-     * Get the default buffer size. Can be overridden.
-     *
-     * @return the default buffer size.
-     */
-    protected int getDefaultBufferSize() {
-        return DEFAULT_BUFFER_SIZE;
-    }
-
-    /**
-     * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}.
-     * @param context the context to be used
-     * @param minCapacity the minimum required capacity
-     * @return the resized byte[] buffer
-     * @throws OutOfMemoryError if the {@code minCapacity} is negative
-     */
-    private static byte[] resizeBuffer(final Context context, final int minCapacity) {
-        // Overflow-conscious code treats the min and new capacity as unsigned.
-        final int oldCapacity = context.buffer.length;
-        int newCapacity = oldCapacity * DEFAULT_BUFFER_RESIZE_FACTOR;
-        if (compareUnsigned(newCapacity, minCapacity) < 0) {
-            newCapacity = minCapacity;
-        }
-        if (compareUnsigned(newCapacity, MAX_BUFFER_SIZE) > 0) {
-            newCapacity = createPositiveCapacity(minCapacity);
-        }
-
-        final byte[] b = new byte[newCapacity];
-        System.arraycopy(context.buffer, 0, b, 0, context.buffer.length);
-        context.buffer = b;
-        return b;
-    }
-
-    /**
-     * Compares two {@code int} values numerically treating the values
-     * as unsigned. Taken from JDK 1.8.
-     *
-     * <p>TODO: Replace with JDK 1.8 Integer::compareUnsigned(int, int).</p>
-     *
-     * @param  x the first {@code int} to compare
-     * @param  y the second {@code int} to compare
-     * @return the value {@code 0} if {@code x == y}; a value less
-     *         than {@code 0} if {@code x < y} as unsigned values; and
-     *         a value greater than {@code 0} if {@code x > y} as
-     *         unsigned values
-     */
-    private static int compareUnsigned(final int x, final int y) {
-        return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
-    }
-
-    /**
-     * Create a positive capacity at least as large the minimum required capacity.
-     * If the minimum capacity is negative then this throws an OutOfMemoryError as no array
-     * can be allocated.
-     *
-     * @param minCapacity the minimum capacity
-     * @return the capacity
-     * @throws OutOfMemoryError if the {@code minCapacity} is negative
-     */
-    private static int createPositiveCapacity(final int minCapacity) {
-        if (minCapacity < 0) {
-            // overflow
-            throw new OutOfMemoryError("Unable to allocate array size: " + (minCapacity & 0xffffffffL));
-        }
-        // This is called when we require buffer expansion to a very big array.
-        // Use the conservative maximum buffer size if possible, otherwise the biggest required.
-        //
-        // Note: In this situation JDK 1.8 java.util.ArrayList returns Integer.MAX_VALUE.
-        // This excludes some VMs that can exceed MAX_BUFFER_SIZE but not allocate a full
-        // Integer.MAX_VALUE length array.
-        // The result is that we may have to allocate an array of this size more than once if
-        // the capacity must be expanded again.
-        return (minCapacity > MAX_BUFFER_SIZE) ?
-            minCapacity :
-            MAX_BUFFER_SIZE;
-    }
-
-    /**
-     * Ensure that the buffer has room for {@code size} bytes
-     *
-     * @param size minimum spare space required
-     * @param context the context to be used
-     * @return the buffer
-     */
-    protected byte[] ensureBufferSize(final int size, final Context context){
-        if (context.buffer == null) {
-            context.buffer = new byte[getDefaultBufferSize()];
-            context.pos = 0;
-            context.readPos = 0;
-
-            // Overflow-conscious:
-            // x + y > z  ==  x + y - z > 0
-        } else if (context.pos + size - context.buffer.length > 0) {
-            return resizeBuffer(context, context.pos + size);
-        }
-        return context.buffer;
-    }
-
-    /**
-     * 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.
-     * <p>
-     * Package protected for access from I/O streams.
-     *
-     * @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).
-     * @param context
-     *            the context to be used
-     * @return The number of bytes successfully extracted into the provided byte[] array.
-     */
-    int readResults(final byte[] b, final int bPos, final int bAvail, final Context context) {
-        if (context.buffer != null) {
-            final int len = Math.min(available(context), bAvail);
-            System.arraycopy(context.buffer, context.readPos, b, bPos, len);
-            context.readPos += len;
-            if (context.readPos >= context.pos) {
-                context.buffer = null; // so hasData() will return false, and this method can return -1
-            }
-            return len;
-        }
-        return context.eof ? EOF : 0;
-    }
-
-    /**
-     * Gets a copy of the chunk separator per RFC 2045 section 2.1.
+     * Tests a given byte array to see if it contains any characters within the alphabet or PAD.
      *
-     * @return the chunk separator
-     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
-     * @since 1.15
-     */
-    public static byte[] getChunkSeparator() {
-        return CHUNK_SEPARATOR.clone();
-    }
-
-    /**
-     * Checks if a byte value is whitespace or not.
-     * Whitespace is taken to mean: space, tab, CR, LF
-     * @param byteToCheck
-     *            the byte to check
-     * @return true if byte is whitespace, false otherwise
-     */
-    protected static boolean isWhiteSpace(final byte byteToCheck) {
-        switch (byteToCheck) {
-            case ' ' :
-            case '\n' :
-            case '\r' :
-            case '\t' :
-                return true;
-            default :
-                return false;
-        }
-    }
-
-    /**
-     * Encodes an Object using the Base-N 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[].
+     * Intended for use in checking line-ending arrays
      *
-     * @param obj
-     *            Object to encode
-     * @return An object (of type byte[]) containing the Base-N encoded data which corresponds to the byte[] supplied.
-     * @throws EncoderException
-     *             if the parameter supplied is not of type byte[]
+     * @param arrayOctet
+     *            byte array to test
+     * @return {@code true} if any byte is a valid character in the alphabet or PAD; {@code false} otherwise
      */
-    @Override
-    public Object encode(final Object obj) throws EncoderException {
-        if (!(obj instanceof byte[])) {
-            throw new EncoderException("Parameter supplied to Base-N encode is not a byte[]");
+    protected boolean containsAlphabetOrPad(final byte[] arrayOctet) {
+        if (arrayOctet == null) {
+            return false;
         }
-        return encode((byte[]) obj);
+        for (final byte element : arrayOctet) {
+            if (pad == element || isInAlphabet(element)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
-     * Encodes a byte[] containing binary data, into a String containing characters in the Base-N alphabet.
-     * Uses UTF8 encoding.
+     * Decodes a byte[] containing characters in the Base-N alphabet.
      *
      * @param pArray
-     *            a byte array containing binary data
-     * @return A String containing only Base-N character data
+     *            A byte array containing Base-N character data
+     * @return a byte array containing binary data
      */
-    public String encodeToString(final byte[] pArray) {
-        return StringUtils.newStringUtf8(encode(pArray));
+    @Override
+    public byte[] decode(final byte[] pArray) {
+        if (pArray == null || pArray.length == 0) {
+            return pArray;
+        }
+        final Context context = new Context();
+        decode(pArray, 0, pArray.length, context);
+        decode(pArray, 0, EOF, context); // Notify decoder of EOF.
+        final byte[] result = new byte[context.pos];
+        readResults(result, 0, result.length, context);
+        return result;
     }
 
-    /**
-     * Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet.
-     * Uses UTF8 encoding.
-     *
-     * @param pArray a byte array containing binary data
-     * @return String containing only character data in the appropriate alphabet.
-     * @since 1.5
-     * This is a duplicate of {@link #encodeToString(byte[])}; it was merged during refactoring.
-    */
-    public String encodeAsString(final byte[] pArray){
-        return StringUtils.newStringUtf8(encode(pArray));
-    }
+    // package protected for access from I/O streams
+    abstract void decode(byte[] pArray, int i, int length, Context context);
 
     /**
      * Decodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of
@@ -572,26 +473,6 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
     }
 
     /**
-     * Decodes a byte[] containing characters in the Base-N alphabet.
-     *
-     * @param pArray
-     *            A byte array containing Base-N character data
-     * @return a byte array containing binary data
-     */
-    @Override
-    public byte[] decode(final byte[] pArray) {
-        if (pArray == null || pArray.length == 0) {
-            return pArray;
-        }
-        final Context context = new Context();
-        decode(pArray, 0, pArray.length, context);
-        decode(pArray, 0, EOF, context); // Notify decoder of EOF.
-        final byte[] result = new byte[context.pos];
-        readResults(result, 0, result.length, context);
-        return result;
-    }
-
-    /**
      * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet.
      *
      * @param pArray
@@ -634,8 +515,122 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
     // package protected for access from I/O streams
     abstract void encode(byte[] pArray, int i, int length, Context context);
 
-    // package protected for access from I/O streams
-    abstract void decode(byte[] pArray, int i, int length, Context context);
+    /**
+     * Encodes an Object using the Base-N 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[].
+     *
+     * @param obj
+     *            Object to encode
+     * @return An object (of type byte[]) containing the Base-N encoded data which corresponds to the byte[] supplied.
+     * @throws EncoderException
+     *             if the parameter supplied is not of type byte[]
+     */
+    @Override
+    public Object encode(final Object obj) throws EncoderException {
+        if (!(obj instanceof byte[])) {
+            throw new EncoderException("Parameter supplied to Base-N encode is not a byte[]");
+        }
+        return encode((byte[]) obj);
+    }
+
+    /**
+     * Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet.
+     * Uses UTF8 encoding.
+     *
+     * @param pArray a byte array containing binary data
+     * @return String containing only character data in the appropriate alphabet.
+     * @since 1.5
+     * This is a duplicate of {@link #encodeToString(byte[])}; it was merged during refactoring.
+    */
+    public String encodeAsString(final byte[] pArray){
+        return StringUtils.newStringUtf8(encode(pArray));
+    }
+
+    /**
+     * Encodes a byte[] containing binary data, into a String containing characters in the Base-N alphabet.
+     * Uses UTF8 encoding.
+     *
+     * @param pArray
+     *            a byte array containing binary data
+     * @return A String containing only Base-N character data
+     */
+    public String encodeToString(final byte[] pArray) {
+        return StringUtils.newStringUtf8(encode(pArray));
+    }
+
+    /**
+     * Ensure that the buffer has room for {@code size} bytes
+     *
+     * @param size minimum spare space required
+     * @param context the context to be used
+     * @return the buffer
+     */
+    protected byte[] ensureBufferSize(final int size, final Context context){
+        if (context.buffer == null) {
+            context.buffer = new byte[getDefaultBufferSize()];
+            context.pos = 0;
+            context.readPos = 0;
+
+            // Overflow-conscious:
+            // x + y > z  ==  x + y - z > 0
+        } else if (context.pos + size - context.buffer.length > 0) {
+            return resizeBuffer(context, context.pos + size);
+        }
+        return context.buffer;
+    }
+
+    /**
+     * Returns the decoding behavior policy. Decoding will raise an {@link IllegalArgumentException} if trailing bits
+     * are not part of a valid encoding.
+     *
+     * <p>
+     * The default is lenient. Decoding will compose trailing bits into 8-bit bytes and discard the remainder.
+     * </p>
+     *
+     * @return true if using strict decoding
+     * @since 1.15
+     */
+    public CodecPolicy getCodecPolicy() {
+        return decodingPolicy;
+    }
+
+    /**
+     * Get the default buffer size. Can be overridden.
+     *
+     * @return the default buffer size.
+     */
+    protected int getDefaultBufferSize() {
+        return DEFAULT_BUFFER_SIZE;
+    }
+
+    /**
+     * Calculates the amount of space needed to encode the supplied array.
+     *
+     * @param pArray byte[] array which will later be encoded
+     *
+     * @return amount of space needed to encoded the supplied array.
+     * Returns a long since a max-len array will require &gt; Integer.MAX_VALUE
+     */
+    public long getEncodedLength(final byte[] pArray) {
+        // Calculate non-chunked size - rounded up to allow for padding
+        // cast to long is needed to avoid possibility of overflow
+        long len = ((pArray.length + unencodedBlockSize-1)  / unencodedBlockSize) * (long) encodedBlockSize;
+        if (lineLength > 0) { // We're using chunking
+            // Round up to nearest multiple
+            len += ((len + lineLength-1) / lineLength) * chunkSeparatorLength;
+        }
+        return len;
+    }
+
+    /**
+     * Returns true if this object has buffered data for reading.
+     *
+     * @param context the context to be used
+     * @return true if there is data still available for reading.
+     */
+    boolean hasData(final Context context) {  // package protected for access from I/O streams
+        return context.buffer != null;
+    }
 
     /**
      * Returns whether or not the {@code octet} is in the current alphabet.
@@ -681,42 +676,47 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
     }
 
     /**
-     * Tests a given byte array to see if it contains any characters within the alphabet or PAD.
+     * Returns true if decoding behavior is strict. Decoding will raise an {@link IllegalArgumentException} if trailing
+     * bits are not part of a valid encoding.
      *
-     * Intended for use in checking line-ending arrays
+     * <p>
+     * The default is false for lenient encoding. Decoding will compose trailing bits into 8-bit bytes and discard the
+     * remainder.
+     * </p>
      *
-     * @param arrayOctet
-     *            byte array to test
-     * @return {@code true} if any byte is a valid character in the alphabet or PAD; {@code false} otherwise
+     * @return true if using strict decoding
+     * @since 1.15
      */
-    protected boolean containsAlphabetOrPad(final byte[] arrayOctet) {
-        if (arrayOctet == null) {
-            return false;
-        }
-        for (final byte element : arrayOctet) {
-            if (pad == element || isInAlphabet(element)) {
-                return true;
-            }
-        }
-        return false;
+    public boolean isStrictDecoding() {
+        return decodingPolicy == CodecPolicy.STRICT;
     }
 
     /**
-     * Calculates the amount of space needed to encode the supplied array.
-     *
-     * @param pArray byte[] array which will later be encoded
+     * 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.
+     * <p>
+     * Package protected for access from I/O streams.
      *
-     * @return amount of space needed to encoded the supplied array.
-     * Returns a long since a max-len array will require &gt; Integer.MAX_VALUE
+     * @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).
+     * @param context
+     *            the context to be used
+     * @return The number of bytes successfully extracted into the provided byte[] array.
      */
-    public long getEncodedLength(final byte[] pArray) {
-        // Calculate non-chunked size - rounded up to allow for padding
-        // cast to long is needed to avoid possibility of overflow
-        long len = ((pArray.length + unencodedBlockSize-1)  / unencodedBlockSize) * (long) encodedBlockSize;
-        if (lineLength > 0) { // We're using chunking
-            // Round up to nearest multiple
-            len += ((len + lineLength-1) / lineLength) * chunkSeparatorLength;
+    int readResults(final byte[] b, final int bPos, final int bAvail, final Context context) {
+        if (context.buffer != null) {
+            final int len = Math.min(available(context), bAvail);
+            System.arraycopy(context.buffer, context.readPos, b, bPos, len);
+            context.readPos += len;
+            if (context.readPos >= context.pos) {
+                context.buffer = null; // so hasData() will return false, and this method can return -1
+            }
+            return len;
         }
-        return len;
+        return context.eof ? EOF : 0;
     }
 }
diff --git a/src/main/java/org/apache/commons/codec/binary/BaseNCodecInputStream.java b/src/main/java/org/apache/commons/codec/binary/BaseNCodecInputStream.java
index 90792f1..ff99e5c 100644
--- a/src/main/java/org/apache/commons/codec/binary/BaseNCodecInputStream.java
+++ b/src/main/java/org/apache/commons/codec/binary/BaseNCodecInputStream.java
@@ -48,20 +48,6 @@ public class BaseNCodecInputStream extends FilterInputStream {
     }
 
     /**
-     * Returns true if decoding behavior is strict. Decoding will raise an
-     * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding.
-     *
-     * <p>The default is false for lenient encoding. Decoding will compose trailing bits
-     * into 8-bit bytes and discard the remainder.
-     *
-     * @return true if using strict decoding
-     * @since 1.15
-     */
-    public boolean isStrictDecoding() {
-        return baseNCodec.isStrictDecoding();
-    }
-
-    /**
      * {@inheritDoc}
      *
      * @return {@code 0} if the {@link InputStream} has reached {@code EOF},
@@ -79,6 +65,20 @@ public class BaseNCodecInputStream extends FilterInputStream {
     }
 
     /**
+     * Returns true if decoding behavior is strict. Decoding will raise an
+     * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding.
+     *
+     * <p>The default is false for lenient encoding. Decoding will compose trailing bits
+     * into 8-bit bytes and discard the remainder.
+     *
+     * @return true if using strict decoding
+     * @since 1.15
+     */
+    public boolean isStrictDecoding() {
+        return baseNCodec.isStrictDecoding();
+    }
+
+    /**
      * Marks the current position in this input stream.
      * <p>The {@link #mark} method of {@link BaseNCodecInputStream} does nothing.</p>
      *
diff --git a/src/main/java/org/apache/commons/codec/binary/BaseNCodecOutputStream.java b/src/main/java/org/apache/commons/codec/binary/BaseNCodecOutputStream.java
index bc27e07..074f96d 100644
--- a/src/main/java/org/apache/commons/codec/binary/BaseNCodecOutputStream.java
+++ b/src/main/java/org/apache/commons/codec/binary/BaseNCodecOutputStream.java
@@ -61,66 +61,49 @@ public class BaseNCodecOutputStream extends FilterOutputStream {
     }
 
     /**
-     * Returns true if decoding behavior is strict. Decoding will raise an
-     * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding.
-     *
-     * <p>The default is false for lenient encoding. Decoding will compose trailing bits
-     * into 8-bit bytes and discard the remainder.
+     * Closes this output stream and releases any system resources associated with the stream.
+     * <p>
+     * To write the EOF marker without closing the stream, call {@link #eof()} or use an
+     * <a href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href=
+     * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html"
+     * >CloseShieldOutputStream</a>.
+     * </p>
      *
-     * @return true if using strict decoding
-     * @since 1.15
+     * @throws IOException
+     *             if an I/O error occurs.
      */
-    public boolean isStrictDecoding() {
-        return baseNCodec.isStrictDecoding();
+    @Override
+    public void close() throws IOException {
+        eof();
+        flush();
+        out.close();
     }
 
     /**
-     * Writes the specified {@code byte} to this output stream.
+     * Writes EOF.
      *
-     * @param i
-     *            source byte
      * @throws IOException
      *             if an I/O error occurs.
+     * @since 1.11
      */
-    @Override
-    public void write(final int i) throws IOException {
-        singleByte[0] = (byte) i;
-        write(singleByte, 0, 1);
+    public void eof() throws IOException {
+        // Notify encoder of EOF (-1).
+        if (doEncode) {
+            baseNCodec.encode(singleByte, 0, EOF, context);
+        } else {
+            baseNCodec.decode(singleByte, 0, EOF, context);
+        }
     }
 
     /**
-     * Writes {@code len} bytes from the specified {@code b} array starting at {@code offset} to this
-     * output stream.
-     *
-     * @param array
-     *            source byte array
-     * @param offset
-     *            where to start reading the bytes
-     * @param len
-     *            maximum number of bytes to write
+     * Flushes this output stream and forces any buffered output bytes to be written out to the stream.
      *
      * @throws IOException
      *             if an I/O error occurs.
-     * @throws NullPointerException
-     *             if the byte array parameter is null
-     * @throws IndexOutOfBoundsException
-     *             if offset, len or buffer size are invalid
      */
     @Override
-    public void write(final byte array[], final int offset, final int len) throws IOException {
-        Objects.requireNonNull(array, "array");
-        if (offset < 0 || len < 0) {
-            throw new IndexOutOfBoundsException();
-        } else if (offset > array.length || offset + len > array.length) {
-            throw new IndexOutOfBoundsException();
-        } else if (len > 0) {
-            if (doEncode) {
-                baseNCodec.encode(array, offset, len, context);
-            } else {
-                baseNCodec.decode(array, offset, len, context);
-            }
-            flush(false);
-        }
+    public void flush() throws IOException {
+        flush(true);
     }
 
     /**
@@ -147,49 +130,66 @@ public class BaseNCodecOutputStream extends FilterOutputStream {
     }
 
     /**
-     * Flushes this output stream and forces any buffered output bytes to be written out to the stream.
+     * Returns true if decoding behavior is strict. Decoding will raise an
+     * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding.
      *
-     * @throws IOException
-     *             if an I/O error occurs.
+     * <p>The default is false for lenient encoding. Decoding will compose trailing bits
+     * into 8-bit bytes and discard the remainder.
+     *
+     * @return true if using strict decoding
+     * @since 1.15
      */
-    @Override
-    public void flush() throws IOException {
-        flush(true);
+    public boolean isStrictDecoding() {
+        return baseNCodec.isStrictDecoding();
     }
 
     /**
-     * Closes this output stream and releases any system resources associated with the stream.
-     * <p>
-     * To write the EOF marker without closing the stream, call {@link #eof()} or use an
-     * <a href="https://commons.apache.org/proper/commons-io/">Apache Commons IO</a> <a href=
-     * "https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html"
-     * >CloseShieldOutputStream</a>.
-     * </p>
+     * Writes {@code len} bytes from the specified {@code b} array starting at {@code offset} to this
+     * output stream.
+     *
+     * @param array
+     *            source byte array
+     * @param offset
+     *            where to start reading the bytes
+     * @param len
+     *            maximum number of bytes to write
      *
      * @throws IOException
      *             if an I/O error occurs.
+     * @throws NullPointerException
+     *             if the byte array parameter is null
+     * @throws IndexOutOfBoundsException
+     *             if offset, len or buffer size are invalid
      */
     @Override
-    public void close() throws IOException {
-        eof();
-        flush();
-        out.close();
+    public void write(final byte array[], final int offset, final int len) throws IOException {
+        Objects.requireNonNull(array, "array");
+        if (offset < 0 || len < 0) {
+            throw new IndexOutOfBoundsException();
+        } else if (offset > array.length || offset + len > array.length) {
+            throw new IndexOutOfBoundsException();
+        } else if (len > 0) {
+            if (doEncode) {
+                baseNCodec.encode(array, offset, len, context);
+            } else {
+                baseNCodec.decode(array, offset, len, context);
+            }
+            flush(false);
+        }
     }
 
     /**
-     * Writes EOF.
+     * Writes the specified {@code byte} to this output stream.
      *
+     * @param i
+     *            source byte
      * @throws IOException
      *             if an I/O error occurs.
-     * @since 1.11
      */
-    public void eof() throws IOException {
-        // Notify encoder of EOF (-1).
-        if (doEncode) {
-            baseNCodec.encode(singleByte, 0, EOF, context);
-        } else {
-            baseNCodec.decode(singleByte, 0, EOF, context);
-        }
+    @Override
+    public void write(final int i) throws IOException {
+        singleByte[0] = (byte) i;
+        write(singleByte, 0, 1);
     }
 
 }
diff --git a/src/main/java/org/apache/commons/codec/binary/BinaryCodec.java b/src/main/java/org/apache/commons/codec/binary/BinaryCodec.java
index 0c2389a..590dd3e 100644
--- a/src/main/java/org/apache/commons/codec/binary/BinaryCodec.java
+++ b/src/main/java/org/apache/commons/codec/binary/BinaryCodec.java
@@ -70,105 +70,14 @@ public class BinaryCodec implements BinaryDecoder, BinaryEncoder {
     private static final int[] BITS = {BIT_0, BIT_1, BIT_2, BIT_3, BIT_4, BIT_5, BIT_6, BIT_7};
 
     /**
-     * Converts an array of raw binary data into an array of ASCII 0 and 1 characters.
-     *
-     * @param raw
-     *                  the raw binary data to convert
-     * @return 0 and 1 ASCII character bytes one for each bit of the argument
-     * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
-     */
-    @Override
-    public byte[] encode(final byte[] raw) {
-        return toAsciiBytes(raw);
-    }
-
-    /**
-     * Converts an array of raw binary data into an array of ASCII 0 and 1 chars.
-     *
-     * @param raw
-     *                  the raw binary data to convert
-     * @return 0 and 1 ASCII character chars one for each bit of the argument
-     * @throws EncoderException
-     *                  if the argument is not a byte[]
-     * @see org.apache.commons.codec.Encoder#encode(Object)
-     */
-    @Override
-    public Object encode(final Object raw) throws EncoderException {
-        if (!(raw instanceof byte[])) {
-            throw new EncoderException("argument not a byte array");
-        }
-        return toAsciiChars((byte[]) raw);
-    }
-
-    /**
-     * Decodes a byte array where each byte represents an ASCII '0' or '1'.
-     *
-     * @param ascii
-     *                  each byte represents an ASCII '0' or '1'
-     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
-     * @throws DecoderException
-     *                  if argument is not a byte[], char[] or String
-     * @see org.apache.commons.codec.Decoder#decode(Object)
-     */
-    @Override
-    public Object decode(final Object ascii) throws DecoderException {
-        if (ascii == null) {
-            return EMPTY_BYTE_ARRAY;
-        }
-        if (ascii instanceof byte[]) {
-            return fromAscii((byte[]) ascii);
-        }
-        if (ascii instanceof char[]) {
-            return fromAscii((char[]) ascii);
-        }
-        if (ascii instanceof String) {
-            return fromAscii(((String) ascii).toCharArray());
-        }
-        throw new DecoderException("argument not a byte array");
-    }
-
-    /**
      * Decodes a byte array where each byte represents an ASCII '0' or '1'.
      *
      * @param ascii
      *                  each byte represents an ASCII '0' or '1'
      * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
-     * @see org.apache.commons.codec.Decoder#decode(Object)
      */
-    @Override
-    public byte[] decode(final byte[] ascii) {
-        return fromAscii(ascii);
-    }
-
-    /**
-     * Decodes a String where each char of the String represents an ASCII '0' or '1'.
-     *
-     * @param ascii
-     *                  String of '0' and '1' characters
-     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
-     * @see org.apache.commons.codec.Decoder#decode(Object)
-     */
-    public byte[] toByteArray(final String ascii) {
-        if (ascii == null) {
-            return EMPTY_BYTE_ARRAY;
-        }
-        return fromAscii(ascii.toCharArray());
-    }
-
-    // ------------------------------------------------------------------------
-    //
-    // static codec operations
-    //
-    // ------------------------------------------------------------------------
-    /**
-     * Decodes a char array where each char represents an ASCII '0' or '1'.
-     *
-     * @param ascii
-     *                  each char represents an ASCII '0' or '1'
-     * @return the raw encoded binary where each bit corresponds to a char in the char array argument
-     */
-    public static byte[] fromAscii(final char[] ascii) {
-        if (ascii == null || ascii.length == 0) {
+    public static byte[] fromAscii(final byte[] ascii) {
+        if (isEmpty(ascii)) {
             return EMPTY_BYTE_ARRAY;
         }
         // get length/8 times bytes with 3 bit shifts to the right of the length
@@ -187,15 +96,20 @@ public class BinaryCodec implements BinaryDecoder, BinaryEncoder {
         return l_raw;
     }
 
+    // ------------------------------------------------------------------------
+    //
+    // static codec operations
+    //
+    // ------------------------------------------------------------------------
     /**
-     * Decodes a byte array where each byte represents an ASCII '0' or '1'.
+     * Decodes a char array where each char represents an ASCII '0' or '1'.
      *
      * @param ascii
-     *                  each byte represents an ASCII '0' or '1'
-     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+     *                  each char represents an ASCII '0' or '1'
+     * @return the raw encoded binary where each bit corresponds to a char in the char array argument
      */
-    public static byte[] fromAscii(final byte[] ascii) {
-        if (isEmpty(ascii)) {
+    public static byte[] fromAscii(final char[] ascii) {
+        if (ascii == null || ascii.length == 0) {
             return EMPTY_BYTE_ARRAY;
         }
         // get length/8 times bytes with 3 bit shifts to the right of the length
@@ -297,4 +211,90 @@ public class BinaryCodec implements BinaryDecoder, BinaryEncoder {
     public static String toAsciiString(final byte[] raw) {
         return new String(toAsciiChars(raw));
     }
+
+    /**
+     * Decodes a byte array where each byte represents an ASCII '0' or '1'.
+     *
+     * @param ascii
+     *                  each byte represents an ASCII '0' or '1'
+     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+     * @see org.apache.commons.codec.Decoder#decode(Object)
+     */
+    @Override
+    public byte[] decode(final byte[] ascii) {
+        return fromAscii(ascii);
+    }
+
+    /**
+     * Decodes a byte array where each byte represents an ASCII '0' or '1'.
+     *
+     * @param ascii
+     *                  each byte represents an ASCII '0' or '1'
+     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+     * @throws DecoderException
+     *                  if argument is not a byte[], char[] or String
+     * @see org.apache.commons.codec.Decoder#decode(Object)
+     */
+    @Override
+    public Object decode(final Object ascii) throws DecoderException {
+        if (ascii == null) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        if (ascii instanceof byte[]) {
+            return fromAscii((byte[]) ascii);
+        }
+        if (ascii instanceof char[]) {
+            return fromAscii((char[]) ascii);
+        }
+        if (ascii instanceof String) {
+            return fromAscii(((String) ascii).toCharArray());
+        }
+        throw new DecoderException("argument not a byte array");
+    }
+
+    /**
+     * Converts an array of raw binary data into an array of ASCII 0 and 1 characters.
+     *
+     * @param raw
+     *                  the raw binary data to convert
+     * @return 0 and 1 ASCII character bytes one for each bit of the argument
+     * @see org.apache.commons.codec.BinaryEncoder#encode(byte[])
+     */
+    @Override
+    public byte[] encode(final byte[] raw) {
+        return toAsciiBytes(raw);
+    }
+
+    /**
+     * Converts an array of raw binary data into an array of ASCII 0 and 1 chars.
+     *
+     * @param raw
+     *                  the raw binary data to convert
+     * @return 0 and 1 ASCII character chars one for each bit of the argument
+     * @throws EncoderException
+     *                  if the argument is not a byte[]
+     * @see org.apache.commons.codec.Encoder#encode(Object)
+     */
+    @Override
+    public Object encode(final Object raw) throws EncoderException {
+        if (!(raw instanceof byte[])) {
+            throw new EncoderException("argument not a byte array");
+        }
+        return toAsciiChars((byte[]) raw);
+    }
+
+    /**
+     * Decodes a String where each char of the String represents an ASCII '0' or '1'.
+     *
+     * @param ascii
+     *                  String of '0' and '1' characters
+     * @return the raw encoded binary where each bit corresponds to a byte in the byte array argument
+     * @see org.apache.commons.codec.Decoder#decode(Object)
+     */
+    public byte[] toByteArray(final String ascii) {
+        if (ascii == null) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        return fromAscii(ascii.toCharArray());
+    }
 }
diff --git a/src/main/java/org/apache/commons/codec/binary/Hex.java b/src/main/java/org/apache/commons/codec/binary/Hex.java
index 736fa1d..57c05bd 100644
--- a/src/main/java/org/apache/commons/codec/binary/Hex.java
+++ b/src/main/java/org/apache/commons/codec/binary/Hex.java
@@ -64,20 +64,6 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
             'E', 'F' };
 
     /**
-     * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array
-     * will be half the length of the passed String, as it takes two characters to represent any given byte. An
-     * exception is thrown if the passed String has an odd number of elements.
-     *
-     * @param data A String containing hexadecimal digits
-     * @return A byte array containing binary data decoded from the supplied char array.
-     * @throws DecoderException Thrown if an odd number or illegal of characters is supplied
-     * @since 1.11
-     */
-    public static byte[] decodeHex(final String data) throws DecoderException {
-        return decodeHex(data.toCharArray());
-    }
-
-    /**
      * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
      * returned array will be half the length of the passed array, as it takes two characters to represent any given
      * byte. An exception is thrown if the passed char array has an odd number of elements.
@@ -109,30 +95,28 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
     }
 
     /**
-     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
-     * The returned array will be double the length of the passed array, as it takes two characters to represent any
-     * given byte.
+     * Converts a String representing hexadecimal values into an array of bytes of those same values. The returned array
+     * will be half the length of the passed String, as it takes two characters to represent any given byte. An
+     * exception is thrown if the passed String has an odd number of elements.
      *
-     * @param data a byte[] to convert to Hex characters
-     * @return A char[] containing lower-case hexadecimal characters
+     * @param data A String containing hexadecimal digits
+     * @return A byte array containing binary data decoded from the supplied char array.
+     * @throws DecoderException Thrown if an odd number or illegal of characters is supplied
+     * @since 1.11
      */
-    public static char[] encodeHex(final byte[] data) {
-        return encodeHex(data, true);
+    public static byte[] decodeHex(final String data) throws DecoderException {
+        return decodeHex(data.toCharArray());
     }
 
     /**
-     * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
-     * returned array will be double the length of the passed array, as it takes two characters to represent any given
-     * byte.
-     *
-     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
-     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
+     * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
+     * The returned array will be double the length of the passed array, as it takes two characters to represent any
+     * given byte.
      *
-     * @param data a byte buffer to convert to Hex characters
+     * @param data a byte[] to convert to Hex characters
      * @return A char[] containing lower-case hexadecimal characters
-     * @since 1.11
      */
-    public static char[] encodeHex(final ByteBuffer data) {
+    public static char[] encodeHex(final byte[] data) {
         return encodeHex(data, true);
     }
 
@@ -151,23 +135,6 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
     }
 
     /**
-     * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
-     * returned array will be double the length of the passed array, as it takes two characters to represent any given
-     * byte.
-     *
-     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
-     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
-     *
-     * @param data        a byte buffer to convert to Hex characters
-     * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
-     * @return A char[] containing hexadecimal characters in the selected case
-     * @since 1.11
-     */
-    public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) {
-        return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
-    }
-
-    /**
      * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
      * The returned array will be double the length of the passed array, as it takes two characters to represent any
      * given byte.
@@ -197,6 +164,39 @@ public class Hex implements BinaryEncoder, BinaryDecoder {
      * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
      * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
      *
+     * @param data a byte buffer to convert to Hex characters
+     * @return A char[] containing lower-case hexadecimal characters
+     * @since 1.11
+     */
+    public static char[] encodeHex(final ByteBuffer data) {
+        return encodeHex(data, true);
+    }
+
+    /**
+     * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
+     * returned array will be double the length of the passed array, as it takes two characters to represent any given
+     * byte.
+     *
+     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
+     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
+     *
+     * @param data        a byte buffer to convert to Hex characters
+     * @param toLowerCase {@code true} converts to lowercase, {@code false} to uppercase
+     * @return A char[] containing hexadecimal characters in the selected case
+     * @since 1.11
+     */
+    public static char[] encodeHex(final ByteBuffer data, final boolean toLowerCase) {
+        return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+    }
+
+    /**
+     * Converts a byte buffer into an array of characters representing the hexadecimal values of each byte in order. The
+     * returned array will be double the length of the passed array, as it takes two characters to represent any given
+     * byte.
+     *
+     * <p>All bytes identified by {@link ByteBuffer#remaining()} will be used; after this method
+     * the value {@link ByteBuffer#remaining() remaining()} will be zero.</p>
+     *
      * @param byteBuffer a byte buffer to convert to Hex characters
      * @param toDigits   the output alphabet (must be at least 16 characters)
      * @return A char[] containing the appropriate characters from the alphabet For best results, this should be either
diff --git a/src/main/java/org/apache/commons/codec/binary/StringUtils.java b/src/main/java/org/apache/commons/codec/binary/StringUtils.java
index 260c77a..176c3d6 100644
--- a/src/main/java/org/apache/commons/codec/binary/StringUtils.java
+++ b/src/main/java/org/apache/commons/codec/binary/StringUtils.java
@@ -89,22 +89,6 @@ public class StringUtils {
      *            The {@link Charset} to encode the {@code String}
      * @return the encoded bytes
      */
-    private static byte[] getBytes(final String string, final Charset charset) {
-        if (string == null) {
-            return null;
-        }
-        return string.getBytes(charset);
-    }
-
-    /**
-     * Calls {@link String#getBytes(Charset)}
-     *
-     * @param string
-     *            The string to encode (if null, return null).
-     * @param charset
-     *            The {@link Charset} to encode the {@code String}
-     * @return the encoded bytes
-     */
     private static ByteBuffer getByteBuffer(final String string, final Charset charset) {
         if (string == null) {
             return null;
@@ -131,6 +115,22 @@ public class StringUtils {
     }
 
     /**
+     * Calls {@link String#getBytes(Charset)}
+     *
+     * @param string
+     *            The string to encode (if null, return null).
+     * @param charset
+     *            The {@link Charset} to encode the {@code String}
+     * @return the encoded bytes
+     */
+    private static byte[] getBytes(final String string, final Charset charset) {
+        if (string == null) {
+            return null;
+        }
+        return string.getBytes(charset);
+    }
+
+    /**
      * Encodes the given string into a sequence of bytes using the ISO-8859-1 charset, storing the result into a new
      * byte array.
      *