changeset 8933:301d76b8cb55

8028397: Undo the lenient MIME BASE64 decoder support change (JDK-8025003) and remove methods de/encode(buf, buf) Summary: updated the spec and implementation as requested Reviewed-by: alanb
author sherman
date Tue, 03 Dec 2013 17:44:31 -0800
parents 75165f6c1c50
children c6b6b515cf4f
files src/share/classes/java/util/Base64.java test/java/util/Base64/Base64GetEncoderTest.java test/java/util/Base64/TestBase64.java test/java/util/Base64/TestBase64Golden.java
diffstat 4 files changed, 84 insertions(+), 784 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/Base64.java	Tue Dec 03 17:25:28 2013 -0800
+++ b/src/share/classes/java/util/Base64.java	Tue Dec 03 17:44:31 2013 -0800
@@ -351,62 +351,6 @@
         }
 
         /**
-         * Encodes as many bytes as possible from the input byte buffer
-         * using the {@link Base64} encoding scheme, writing the resulting
-         * bytes to the given output byte buffer.
-         *
-         * <p>The buffers are read from, and written to, starting at their
-         * current positions. Upon return, the input and output buffers'
-         * positions will be advanced to reflect the bytes read and written,
-         * but their limits will not be modified.
-         *
-         * <p>The encoding operation will stop and return if either all
-         * remaining bytes in the input buffer have been encoded and written
-         * to the output buffer, or the output buffer has insufficient space
-         * to encode any more input bytes. The encoding operation can be
-         * continued, if there is more bytes in input buffer to be encoded,
-         * by invoking this method again with an output buffer that has more
-         * {@linkplain java.nio.Buffer#remaining remaining} bytes. This is
-         * typically done by draining any encoded bytes from the output buffer.
-         * The value returned from last invocation needs to be passed in as the
-         * third parameter {@code bytesOut} if it is to continue an unfinished
-         * encoding, 0 otherwise.
-         *
-         * <p><b>Recommended Usage Example</b>
-         * <pre>
-         *    ByteBuffer src = ...;
-         *    ByteBuffer dst = ...;
-         *    Base64.Encoder enc = Base64.getMimeDecoder();
-         *
-         *    int bytesOut = 0;
-         *    while (src.hasRemaining()) {
-         *        // clear output buffer for decoding
-         *        dst.clear();
-         *        bytesOut = enc.encode(src, dst, bytesOut);
-         *
-         *        // read encoded bytes out of "dst"
-         *        dst.flip();
-         *        ...
-         *    }
-         * </pre>
-         *
-         * @param   src
-         *          the input byte buffer to encode
-         * @param   dst
-         *          the output byte buffer
-         * @param   bytesOut
-         *          the return value of last invocation if this is to continue
-         *          an unfinished encoding operation, 0 otherwise
-         * @return  The sum total of {@code bytesOut} and the number of bytes
-         *          written to the output ByteBuffer during this invocation.
-         */
-        public int encode(ByteBuffer src, ByteBuffer dst, int bytesOut) {
-            if (src.hasArray() && dst.hasArray())
-                return encodeArray(src, dst, bytesOut);
-            return encodeBuffer(src, dst, bytesOut);
-        }
-
-        /**
          * Wraps an output stream for encoding byte data using the {@link Base64}
          * encoding scheme.
          *
@@ -444,160 +388,6 @@
             return new Encoder(isURL, newline, linemax, false);
         }
 
-        private int encodeArray(ByteBuffer src, ByteBuffer dst, int bytesOut) {
-            char[] base64 = isURL? toBase64URL : toBase64;
-            byte[] sa = src.array();
-            int    sp = src.arrayOffset() + src.position();
-            int    sl = src.arrayOffset() + src.limit();
-            byte[] da = dst.array();
-            int    dp = dst.arrayOffset() + dst.position();
-            int    dl = dst.arrayOffset() + dst.limit();
-            int    dp00 = dp;
-            int    dpos = 0;        // dp of each line
-            if (linemax > 0 && bytesOut > 0)
-                dpos = bytesOut % (linemax + newline.length);
-            try {
-                if (dpos == linemax && sp < src.limit()) {
-                    if (dp + newline.length > dl)
-                        return  dp - dp00 + bytesOut;
-                    for (byte b : newline){
-                        dst.put(dp++, b);
-                    }
-                    dpos = 0;
-                }
-                sl = sp + (sl - sp) / 3 * 3;
-                while (sp < sl) {
-                    int slen = (linemax > 0) ? (linemax - dpos) / 4 * 3
-                                             : sl - sp;
-                    int sl0 = Math.min(sp + slen, sl);
-                    for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
-                        if (dp0 + 4 > dl) {
-                            sp = sp0; dp = dp0;
-                            return  dp0 - dp00 + bytesOut;
-                        }
-                        int bits = (sa[sp0++] & 0xff) << 16 |
-                                   (sa[sp0++] & 0xff) <<  8 |
-                                   (sa[sp0++] & 0xff);
-                        da[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
-                        da[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
-                        da[dp0++] = (byte)base64[(bits >>> 6)  & 0x3f];
-                        da[dp0++] = (byte)base64[bits & 0x3f];
-                    }
-                    int n = (sl0 - sp) / 3 * 4;
-                    dpos += n;
-                    dp += n;
-                    sp = sl0;
-                    if (dpos == linemax && sp < src.limit()) {
-                        if (dp + newline.length > dl)
-                            return  dp - dp00 + bytesOut;
-                        for (byte b : newline){
-                            da[dp++] = b;
-                        }
-                        dpos = 0;
-                    }
-                }
-                sl = src.arrayOffset() + src.limit();
-                if (sp < sl && dl >= dp + 4) {       // 1 or 2 leftover bytes
-                    int b0 = sa[sp++] & 0xff;
-                    da[dp++] = (byte)base64[b0 >> 2];
-                    if (sp == sl) {
-                        da[dp++] = (byte)base64[(b0 << 4) & 0x3f];
-                        if (doPadding) {
-                            da[dp++] = '=';
-                            da[dp++] = '=';
-                        }
-                    } else {
-                        int b1 = sa[sp++] & 0xff;
-                        da[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
-                        da[dp++] = (byte)base64[(b1 << 2) & 0x3f];
-                        if (doPadding) {
-                            da[dp++] = '=';
-                        }
-                    }
-                }
-                return dp - dp00 + bytesOut;
-            } finally {
-                src.position(sp - src.arrayOffset());
-                dst.position(dp - dst.arrayOffset());
-            }
-        }
-
-        private int encodeBuffer(ByteBuffer src, ByteBuffer dst, int bytesOut) {
-            char[] base64 = isURL? toBase64URL : toBase64;
-            int sp = src.position();
-            int sl = src.limit();
-            int dp = dst.position();
-            int dl = dst.limit();
-            int dp00 = dp;
-
-            int dpos = 0;        // dp of each line
-            if (linemax > 0 && bytesOut > 0)
-                dpos = bytesOut % (linemax + newline.length);
-            try {
-                if (dpos == linemax && sp < src.limit()) {
-                    if (dp + newline.length > dl)
-                        return  dp - dp00 + bytesOut;
-                    for (byte b : newline){
-                        dst.put(dp++, b);
-                    }
-                    dpos = 0;
-                }
-                sl = sp + (sl - sp) / 3 * 3;
-                while (sp < sl) {
-                    int slen = (linemax > 0) ? (linemax - dpos) / 4 * 3
-                                             : sl - sp;
-                    int sl0 = Math.min(sp + slen, sl);
-                    for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
-                        if (dp0 + 4 > dl) {
-                            sp = sp0; dp = dp0;
-                            return  dp0 - dp00 + bytesOut;
-                        }
-                        int bits = (src.get(sp0++) & 0xff) << 16 |
-                                   (src.get(sp0++) & 0xff) <<  8 |
-                                   (src.get(sp0++) & 0xff);
-                        dst.put(dp0++, (byte)base64[(bits >>> 18) & 0x3f]);
-                        dst.put(dp0++, (byte)base64[(bits >>> 12) & 0x3f]);
-                        dst.put(dp0++, (byte)base64[(bits >>> 6)  & 0x3f]);
-                        dst.put(dp0++, (byte)base64[bits & 0x3f]);
-                    }
-                    int n = (sl0 - sp) / 3 * 4;
-                    dpos += n;
-                    dp += n;
-                    sp = sl0;
-                    if (dpos == linemax && sp < src.limit()) {
-                        if (dp + newline.length > dl)
-                            return  dp - dp00 + bytesOut;
-                        for (byte b : newline){
-                            dst.put(dp++, b);
-                        }
-                        dpos = 0;
-                    }
-                }
-                if (sp < src.limit() && dl >= dp + 4) {       // 1 or 2 leftover bytes
-                    int b0 = src.get(sp++) & 0xff;
-                    dst.put(dp++, (byte)base64[b0 >> 2]);
-                    if (sp == src.limit()) {
-                        dst.put(dp++, (byte)base64[(b0 << 4) & 0x3f]);
-                        if (doPadding) {
-                            dst.put(dp++, (byte)'=');
-                            dst.put(dp++, (byte)'=');
-                        }
-                    } else {
-                        int b1 = src.get(sp++) & 0xff;
-                        dst.put(dp++, (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
-                        dst.put(dp++, (byte)base64[(b1 << 2) & 0x3f]);
-                        if (doPadding) {
-                            dst.put(dp++, (byte)'=');
-                        }
-                    }
-                }
-                return dp - dp00 + bytesOut;
-            } finally {
-                src.position(sp);
-                dst.position(dp);
-            }
-        }
-
         private int encode0(byte[] src, int off, int end, byte[] dst) {
             char[] base64 = isURL ? toBase64URL : toBase64;
             int sp = off;
@@ -657,20 +447,11 @@
      * required. So if the final unit of the encoded byte data only has
      * two or three Base64 characters (without the corresponding padding
      * character(s) padded), they are decoded as if followed by padding
-     * character(s).
-     * <p>
-     * For decoders that use the <a href="#basic">Basic</a> and
-     * <a href="#url">URL and Filename safe</a> type base64 scheme, and
-     * if there is padding character present in the final unit, the
-     * correct number of padding character(s) must be present, otherwise
-     * {@code IllegalArgumentException} ({@code IOException} when reading
-     * from a Base64 stream) is thrown during decoding.
-     * <p>
-     * Decoders that use the <a href="#mime">MIME</a> type base64 scheme
-     * are more lenient when decoding the padding character(s). If the
-     * padding character(s) is incorrectly encoded, the first padding
-     * character encountered is interpreted as the end of the encoded byte
-     * data, the decoding operation will then end and return normally.
+     * character(s). If there is a padding character present in the
+     * final unit, the correct number of padding character(s) must be
+     * present, otherwise {@code IllegalArgumentException} (
+     * {@code IOException} when reading from a Base64 stream) is thrown
+     * during decoding.
      *
      * <p> Instances of {@link Decoder} class are safe for use by
      * multiple concurrent threads.
@@ -810,6 +591,10 @@
          * output buffer's position will be zero and its limit will be the
          * number of resulting decoded bytes
          *
+         * <p> {@code IllegalArgumentException} is thrown if the input buffer
+         * is not in valid Base64 encoding scheme. The position of the input
+         * buffer will not be advanced in this case.
+         *
          * @param   buffer
          *          the ByteBuffer to decode
          *
@@ -843,76 +628,6 @@
         }
 
         /**
-         * Decodes as many bytes as possible from the input byte buffer
-         * using the {@link Base64} encoding scheme, writing the resulting
-         * bytes to the given output byte buffer.
-         *
-         * <p>The buffers are read from, and written to, starting at their
-         * current positions. Upon return, the input and output buffers'
-         * positions will be advanced to reflect the bytes read and written,
-         * but their limits will not be modified.
-         *
-         * <p> If the input buffer is not in valid Base64 encoding scheme
-         * then some bytes may have been written to the output buffer
-         * before IllegalArgumentException is thrown. The positions of
-         * both input and output buffer will not be advanced in this case.
-         *
-         * <p>The decoding operation will end and return if all remaining
-         * bytes in the input buffer have been decoded and written to the
-         * output buffer.
-         *
-         * <p> The decoding operation will stop and return if the output
-         * buffer has insufficient space to decode any more input bytes.
-         * The decoding operation can be continued, if there is more bytes
-         * in input buffer to be decoded, by invoking this method again with
-         * an output buffer that has more {@linkplain java.nio.Buffer#remaining
-         * remaining} bytes. This is typically done by draining any decoded
-         * bytes from the output buffer.
-         *
-         * <p><b>Recommended Usage Example</b>
-         * <pre>
-         *    ByteBuffer src = ...;
-         *    ByteBuffer dst = ...;
-         *    Base64.Decoder dec = Base64.getDecoder();
-         *
-         *    while (src.hasRemaining()) {
-         *
-         *        // prepare the output byte buffer
-         *        dst.clear();
-         *        dec.decode(src, dst);
-         *
-         *        // read bytes from the output buffer
-         *        dst.flip();
-         *        ...
-         *    }
-         * </pre>
-         *
-         * @param   src
-         *          the input byte buffer to decode
-         * @param   dst
-         *          the output byte buffer
-         *
-         * @return  The number of bytes written to the output byte buffer during
-         *          this decoding invocation
-         *
-         * @throws  IllegalArgumentException
-         *          if {@code src} is not in valid Base64 scheme.
-         */
-        public int decode(ByteBuffer src, ByteBuffer dst) {
-            int sp0 = src.position();
-            int dp0 = dst.position();
-            try {
-                if (src.hasArray() && dst.hasArray())
-                    return decodeArray(src, dst);
-                return decodeBuffer(src, dst);
-            } catch (IllegalArgumentException iae) {
-                src.position(sp0);
-                dst.position(dp0);
-                throw iae;
-            }
-        }
-
-        /**
          * Returns an input stream for decoding {@link Base64} encoded byte stream.
          *
          * <p> The {@code read}  methods of the returned {@code InputStream} will
@@ -932,150 +647,6 @@
             return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
         }
 
-        private int decodeArray(ByteBuffer src, ByteBuffer dst) {
-            int[] base64 = isURL ? fromBase64URL : fromBase64;
-            int   bits = 0;
-            int   shiftto = 18;       // pos of first byte of 4-byte atom
-            byte[] sa = src.array();
-            int    sp = src.arrayOffset() + src.position();
-            int    sl = src.arrayOffset() + src.limit();
-            byte[] da = dst.array();
-            int    dp = dst.arrayOffset() + dst.position();
-            int    dl = dst.arrayOffset() + dst.limit();
-            int    dp0 = dp;
-            int    mark = sp;
-            try {
-                while (sp < sl) {
-                    int b = sa[sp++] & 0xff;
-                    if ((b = base64[b]) < 0) {
-                        if (b == -2) {   // padding byte
-                            if (!isMIME &&
-                                (shiftto == 6 && (sp == sl || sa[sp++] != '=') ||
-                                 shiftto == 18)) {
-                                throw new IllegalArgumentException(
-                                     "Input byte array has wrong 4-byte ending unit");
-                            }
-                            break;
-                        }
-                        if (isMIME)     // skip if for rfc2045
-                            continue;
-                        else
-                            throw new IllegalArgumentException(
-                                "Illegal base64 character " +
-                                Integer.toString(sa[sp - 1], 16));
-                    }
-                    bits |= (b << shiftto);
-                    shiftto -= 6;
-                    if (shiftto < 0) {
-                        if (dl < dp + 3)
-                            return dp - dp0;
-                        da[dp++] = (byte)(bits >> 16);
-                        da[dp++] = (byte)(bits >>  8);
-                        da[dp++] = (byte)(bits);
-                        shiftto = 18;
-                        bits = 0;
-                        mark = sp;
-                    }
-                }
-                if (shiftto == 6) {
-                    if (dl - dp < 1)
-                        return dp - dp0;
-                    da[dp++] = (byte)(bits >> 16);
-                } else if (shiftto == 0) {
-                    if (dl - dp < 2)
-                        return dp - dp0;
-                    da[dp++] = (byte)(bits >> 16);
-                    da[dp++] = (byte)(bits >>  8);
-                } else if (shiftto == 12) {
-                    throw new IllegalArgumentException(
-                        "Last unit does not have enough valid bits");
-                }
-                if (sp < sl) {
-                    if (isMIME)
-                        sp = sl;
-                    else
-                        throw new IllegalArgumentException(
-                            "Input byte array has incorrect ending byte at " + sp);
-                }
-                mark = sp;
-                return dp - dp0;
-            } finally {
-                src.position(mark);
-                dst.position(dp);
-            }
-        }
-
-        private int decodeBuffer(ByteBuffer src, ByteBuffer dst) {
-            int[] base64 = isURL ? fromBase64URL : fromBase64;
-            int   bits = 0;
-            int   shiftto = 18;       // pos of first byte of 4-byte atom
-            int    sp = src.position();
-            int    sl = src.limit();
-            int    dp = dst.position();
-            int    dl = dst.limit();
-            int    dp0 = dp;
-            int    mark = sp;
-            try {
-                while (sp < sl) {
-                    int b = src.get(sp++) & 0xff;
-                    if ((b = base64[b]) < 0) {
-                        if (b == -2) {  // padding byte
-                            if (!isMIME &&
-                                (shiftto == 6 && (sp == sl || src.get(sp++) != '=') ||
-                                 shiftto == 18)) {
-                                throw new IllegalArgumentException(
-                                     "Input byte array has wrong 4-byte ending unit");
-                            }
-                            break;
-                        }
-                        if (isMIME)     // skip if for rfc2045
-                            continue;
-                        else
-                            throw new IllegalArgumentException(
-                                "Illegal base64 character " +
-                                Integer.toString(src.get(sp - 1), 16));
-                    }
-                    bits |= (b << shiftto);
-                    shiftto -= 6;
-                    if (shiftto < 0) {
-                        if (dl < dp + 3)
-                            return dp - dp0;
-                        dst.put(dp++, (byte)(bits >> 16));
-                        dst.put(dp++, (byte)(bits >>  8));
-                        dst.put(dp++, (byte)(bits));
-                        shiftto = 18;
-                        bits = 0;
-                        mark = sp;
-                    }
-                }
-                if (shiftto == 6) {
-                    if (dl - dp < 1)
-                        return dp - dp0;
-                     dst.put(dp++, (byte)(bits >> 16));
-                } else if (shiftto == 0) {
-                    if (dl - dp < 2)
-                        return dp - dp0;
-                    dst.put(dp++, (byte)(bits >> 16));
-                    dst.put(dp++, (byte)(bits >>  8));
-                } else if (shiftto == 12) {
-                    throw new IllegalArgumentException(
-                        "Last unit does not have enough valid bits");
-                }
-                if (sp < sl) {
-                    if (isMIME)
-                        sp = sl;
-                    else
-                        throw new IllegalArgumentException(
-                            "Input byte array has incorrect ending byte at " + sp);
-                }
-                mark = sp;
-                return dp - dp0;
-            } finally {
-                src.position(mark);
-                dst.position(dp);
-            }
-        }
-
         private int outLength(byte[] src, int sp, int sl) {
             int[] base64 = isURL ? fromBase64URL : fromBase64;
             int paddings = 0;
@@ -1123,14 +694,13 @@
                 int b = src[sp++] & 0xff;
                 if ((b = base64[b]) < 0) {
                     if (b == -2) {         // padding byte '='
-                        if (!isMIME  &&    // be lenient for rfc2045
-                            // =     shiftto==18 unnecessary padding
-                            // x=    shiftto==12 a dangling single x
-                            // x     to be handled together with non-padding case
-                            // xx=   shiftto==6&&sp==sl missing last =
-                            // xx=y  shiftto==6 last is not =
-                            (shiftto == 6 && (sp == sl || src[sp++] != '=') ||
-                            shiftto == 18)) {
+                        // =     shiftto==18 unnecessary padding
+                        // x=    shiftto==12 a dangling single x
+                        // x     to be handled together with non-padding case
+                        // xx=   shiftto==6&&sp==sl missing last =
+                        // xx=y  shiftto==6 last is not =
+                        if (shiftto == 6 && (sp == sl || src[sp++] != '=') ||
+                            shiftto == 18) {
                             throw new IllegalArgumentException(
                                 "Input byte array has wrong 4-byte ending unit");
                         }
@@ -1160,14 +730,15 @@
                 dst[dp++] = (byte)(bits >> 16);
                 dst[dp++] = (byte)(bits >>  8);
             } else if (shiftto == 12) {
-                // dangling single "x", throw exception even in lenient mode,
-                // it's incorrectly encoded.
+                // dangling single "x", incorrectly encoded.
                 throw new IllegalArgumentException(
                     "Last unit does not have enough valid bits");
             }
             // anything left is invalid, if is not MIME.
-            // if MIME (lenient), just ignore all leftover
-            if (sp < sl && !isMIME) {
+            // if MIME, ignore all non-base64 character
+            while (sp < sl) {
+                if (isMIME && base64[src[sp++]] < 0)
+                    continue;
                 throw new IllegalArgumentException(
                     "Input byte array has incorrect ending byte at " + sp);
             }
@@ -1367,26 +938,16 @@
                     // xx=y  or last is not '='
                     if (nextin == 18 || nextin == 12 ||
                         nextin == 6 && is.read() != '=') {
-                        if (!isMIME || nextin == 12) {
-                            throw new IOException("Illegal base64 ending sequence:" + nextin);
-                        } else if (nextin != 18) {
-                            // lenient mode for mime
-                            // (1) handle the "unnecessary padding in "xxxx ="
-                            //     case as the eof (nextin == 18)
-                            // (2) decode "xx=" and "xx=y" normally
-                            b[off++] = (byte)(bits >> (16));
-                            len--;
-                        }
-                    } else {
-                        b[off++] = (byte)(bits >> (16));
-                        len--;
-                        if (nextin == 0) {           // only one padding byte
-                            if (len == 0) {          // no enough output space
-                                bits >>= 8;          // shift to lowest byte
-                                nextout = 0;
-                            } else {
-                                b[off++] = (byte) (bits >>  8);
-                            }
+                        throw new IOException("Illegal base64 ending sequence:" + nextin);
+                    }
+                    b[off++] = (byte)(bits >> (16));
+                    len--;
+                    if (nextin == 0) {           // only one padding byte
+                        if (len == 0) {          // no enough output space
+                            bits >>= 8;          // shift to lowest byte
+                            nextout = 0;
+                        } else {
+                            b[off++] = (byte) (bits >>  8);
                         }
                     }
                     eof = true;
--- a/test/java/util/Base64/Base64GetEncoderTest.java	Tue Dec 03 17:25:28 2013 -0800
+++ b/test/java/util/Base64/Base64GetEncoderTest.java	Tue Dec 03 17:44:31 2013 -0800
@@ -51,30 +51,6 @@
 
         testWrapEncode2(encoder);
 
-        testEncodeWithByteBuffer(encoder);
-
-    }
-
-    private static void testEncodeWithByteBuffer(final Base64.Encoder encoder) {
-        System.err.println("\n\nEncoder.encode with ByteBuffer test  ");
-        final byte[] secondTestBuffer =
-                "api/java_util/Base64/index.html#GetEncoderMimeCustom[noLineSeparatorInEncodedString]"
-                .getBytes(US_ASCII);
-        String base64EncodedString;
-        ByteBuffer srcData = ByteBuffer.wrap(secondTestBuffer);
-        ByteBuffer dstData = ByteBuffer.allocate(secondTestBuffer.length * 2);
-
-        encoder.encode(srcData, dstData, 0);
-        dstData.flip();
-        if (dstData.hasArray()) {
-            System.err.println("\nByteBuffer test dstData is Base64 encoding = "
-                    + new String(dstData.array(), US_ASCII) + "\n");
-        }
-
-        base64EncodedString = new String(dstData.array(), US_ASCII);
-        if (base64EncodedString.contains("$$$")) {
-            throw new RuntimeException("Base64 encoding contains line separator after Encoder.encode ByteBuffer ... \n");
-        }
     }
 
     private static void testWrapEncode2(final Base64.Encoder encoder)
--- a/test/java/util/Base64/TestBase64.java	Tue Dec 03 17:25:28 2013 -0800
+++ b/test/java/util/Base64/TestBase64.java	Tue Dec 03 17:44:31 2013 -0800
@@ -23,7 +23,7 @@
 
 /**
  * @test 4235519 8004212 8005394 8007298 8006295 8006315 8006530 8007379 8008925
- *       8014217 8025003 8026330
+ *       8014217 8025003 8026330 8028397
  * @summary tests java.util.Base64
  */
 
@@ -92,6 +92,9 @@
         // illegal line separator
         checkIAE(new Runnable() { public void run() { Base64.getMimeEncoder(10, new byte[]{'\r', 'N'}); }});
 
+        // malformed padding/ending
+        testMalformedPadding();
+
         // illegal base64 character
         decoded[2] = (byte)0xe0;
         checkIAE(new Runnable() {
@@ -100,34 +103,15 @@
             public void run() { Base64.getDecoder().decode(decoded, new byte[1024]); }});
         checkIAE(new Runnable() { public void run() {
             Base64.getDecoder().decode(ByteBuffer.wrap(decoded)); }});
-        checkIAE(new Runnable() { public void run() {
-            Base64.getDecoder().decode(ByteBuffer.wrap(decoded), ByteBuffer.allocate(1024)); }});
-        checkIAE(new Runnable() { public void run() {
-            Base64.getDecoder().decode(ByteBuffer.wrap(decoded), ByteBuffer.allocateDirect(1024)); }});
-
-        // illegal ending unit
-        checkIOE(new Testable() { public void test() throws IOException {
-                                     byte[] bytes = "AA=".getBytes("ASCII");
-                                     try (InputStream stream =
-                                              Base64.getDecoder().wrap(new ByteArrayInputStream(bytes))) {
-                                         while (stream.read() != -1);
-                                     }
-        }});
-
-        // test return value from decode(ByteBuffer, ByteBuffer)
-        testDecBufRet();
 
         // test single-non-base64 character for mime decoding
         testSingleNonBase64MimeDec();
 
         // test decoding of unpadded data
         testDecodeUnpadded();
+
         // test mime decoding with ignored character after padding
         testDecodeIgnoredAfterPadding();
-
-        // lenient mode for ending unit
-        testLenientPadding();
-
     }
 
     private static sun.misc.BASE64Encoder sunmisc = new sun.misc.BASE64Encoder();
@@ -202,24 +186,6 @@
                     if (encoded2 != null)
                         testDecode(dec, ByteBuffer.wrap(encoded2), orig);
 
-                    // -------- testing encode(Buffer, Buffer)--------
-                    testEncode(enc, encoded,
-                               ByteBuffer.wrap(orig),
-                               ByteBuffer.allocate(encoded.length + 10));
-
-                    testEncode(enc, encoded,
-                               ByteBuffer.wrap(orig),
-                               ByteBuffer.allocateDirect(encoded.length + 10));
-
-                    // --------testing decode(Buffer, Buffer);--------
-                    testDecode(dec, orig,
-                               ByteBuffer.wrap(encoded),
-                               ByteBuffer.allocate(orig.length + 10));
-
-                    testDecode(dec, orig,
-                               ByteBuffer.wrap(encoded),
-                               ByteBuffer.allocateDirect(orig.length + 10));
-
                     // --------testing decode.wrap(input stream)--------
                     // 1) random buf length
                     ByteArrayInputStream bais = new ByteArrayInputStream(encoded);
@@ -322,9 +288,7 @@
         checkNull(new Runnable() { public void run() { enc.encode(ba_null, new byte[10]); }});
         checkNull(new Runnable() { public void run() { enc.encode(new byte[10], ba_null); }});
         checkNull(new Runnable() { public void run() { enc.encode(bb_null); }});
-        checkNull(new Runnable() { public void run() { enc.encode(bb_null, ByteBuffer.allocate(10), 0); }});
-        checkNull(new Runnable() { public void run() { enc.encode(ByteBuffer.allocate(10), bb_null, 0); }});
-        checkNull(new Runnable() { public void run() { enc.wrap(null); }});
+        checkNull(new Runnable() { public void run() { enc.wrap((OutputStream)null); }});
     }
 
     private static void testNull(final Base64.Decoder dec) {
@@ -333,9 +297,7 @@
         checkNull(new Runnable() { public void run() { dec.decode(ba_null, new byte[10]); }});
         checkNull(new Runnable() { public void run() { dec.decode(new byte[10], ba_null); }});
         checkNull(new Runnable() { public void run() { dec.decode(bb_null); }});
-        checkNull(new Runnable() { public void run() { dec.decode(bb_null, ByteBuffer.allocate(10)); }});
-        checkNull(new Runnable() { public void run() { dec.decode(ByteBuffer.allocate(10), bb_null); }});
-        checkNull(new Runnable() { public void run() { dec.wrap(null); }});
+        checkNull(new Runnable() { public void run() { dec.wrap((InputStream)null); }});
     }
 
     private static interface Testable {
@@ -412,78 +374,63 @@
                     dec.decode(encoded);
                     throw new RuntimeException("No IAE for non-base64 char");
                 } catch (IllegalArgumentException iae) {}
-
-                // decode(ByteBuffer[], ByteBuffer[])
-                ByteBuffer encodedBB = ByteBuffer.wrap(encoded);
-                ByteBuffer decodedBB = ByteBuffer.allocate(100);
-                int ret = decM.decode(encodedBB, decodedBB);
-                byte[] buf = new byte[ret];
-                decodedBB.flip();
-                decodedBB.get(buf);
-                checkEqual(buf, src[i], "Non-base64 char is not ignored");
-                try {
-                    encodedBB.rewind();
-                    decodedBB.clear();
-                    dec.decode(encodedBB, decodedBB);
-                    throw new RuntimeException("No IAE for non-base64 char");
-                } catch (IllegalArgumentException iae) {}
-                // direct
-                encodedBB.rewind();
-                decodedBB = ByteBuffer.allocateDirect(100);
-                ret = decM.decode(encodedBB, decodedBB);
-                buf = new byte[ret];
-                decodedBB.flip();
-                decodedBB.get(buf);
-                checkEqual(buf, src[i], "Non-base64 char is not ignored");
-                try {
-                    encodedBB.rewind();
-                    decodedBB.clear();
-                    dec.decode(encodedBB, decodedBB);
-                    throw new RuntimeException("No IAE for non-base64 char");
-                } catch (IllegalArgumentException iae) {}
             }
         }
     }
 
-    private static void testLenientPadding() throws Throwable {
-        String[] data = new String[] {
-            "=",         "",        // unnecessary padding
-            "QUJD=",     "ABC",     //"ABC".encode() -> "QUJD"
+    private static void testMalformedPadding() throws Throwable {
+        Object[] data = new Object[] {
+            "$=#",       "",      0,      // illegal ending unit
+            "A",         "",      0,      // dangling single byte
+            "A=",        "",      0,
+            "A==",       "",      0,
+            "QUJDA",     "ABC",   4,
+            "QUJDA=",    "ABC",   4,
+            "QUJDA==",   "ABC",   4,
 
-            "QQ=",       "A",       // incomplete padding
-            "QQ=N",      "A",       // incorrect padding
-            "QQ=?",      "A",
-            "QUJDQQ=",   "ABCA",
-            "QUJDQQ=N",  "ABCA",
-            "QUJDQQ=?",  "ABCA",
+            "=",         "",      0,      // unnecessary padding
+            "QUJD=",     "ABC",   4,      //"ABC".encode() -> "QUJD"
 
-            "QUI=X",     "AB",      // incorrect padding
-            "QUI=?",     "AB",      // incorrect padding
+            "AA=",       "",      0,      // incomplete padding
+            "QQ=",       "",      0,
+            "QQ=N",      "",      0,      // incorrect padding
+            "QQ=?",      "",      0,
+            "QUJDQQ=",   "ABC",   4,
+            "QUJDQQ=N",  "ABC",   4,
+            "QUJDQQ=?",  "ABC",   4,
         };
-        Base64.Decoder dec = Base64.getMimeDecoder();
 
-        for (int i = 0; i < data.length; i += 2) {
-            byte[] src = data[i].getBytes("ASCII");
-            byte[] expected = data[i + 1].getBytes("ASCII");
-            // decode(byte[])
-            byte[] ret = dec.decode(src);
-            checkEqual(ret, expected, "lenient padding decoding failed!");
+        Base64.Decoder[] decs = new Base64.Decoder[] {
+            Base64.getDecoder(),
+            Base64.getUrlDecoder(),
+            Base64.getMimeDecoder()
+        };
 
-            // decode(String)
-            ret = dec.decode(data[i]);
-            checkEqual(ret, expected, "lenient padding decoding failed!");
+        for (Base64.Decoder dec : decs) {
+            for (int i = 0; i < data.length; i += 3) {
+                final String srcStr = (String)data[i];
+                final byte[] srcBytes = srcStr.getBytes("ASCII");
+                final ByteBuffer srcBB = ByteBuffer.wrap(srcBytes);
+                byte[] expected = ((String)data[i + 1]).getBytes("ASCII");
+                int pos = (Integer)data[i + 2];
 
-            // decode(ByteBuffer)
-            ByteBuffer srcBB = ByteBuffer.wrap(src);
-            ByteBuffer retBB = dec.decode(srcBB);
-            checkEqual(srcBB.remaining(), 0, "lenient padding decoding failed!");
-            checkEqual(Arrays.copyOf(retBB.array(), retBB.remaining()),
-                       expected, "lenient padding decoding failed!");
+                // decode(byte[])
+                checkIAE(new Runnable() { public void run() { dec.decode(srcBytes); }});
 
-            // wrap.decode(byte[])
-            ret = new byte[10];
-            int n = dec.wrap(new ByteArrayInputStream(src)).read(ret);
-            checkEqual(Arrays.copyOf(ret, n), expected, "lenient padding decoding failed!");
+                // decode(String)
+                checkIAE(new Runnable() { public void run() { dec.decode(srcStr); }});
+
+                // decode(ByteBuffer)
+                checkIAE(new Runnable() { public void run() { dec.decode(srcBB); }});
+
+                // wrap stream
+                checkIOE(new Testable() {
+                    public void test() throws IOException {
+                        try (InputStream is = dec.wrap(new ByteArrayInputStream(srcBytes))) {
+                            while (is.read() != -1);
+                        }
+                }});
+            }
         }
     }
 
@@ -520,51 +467,6 @@
         }
     }
 
-    private static void testDecBufRet() throws Throwable {
-        Random rnd = new java.util.Random();
-        Base64.Encoder encoder = Base64.getEncoder();
-        Base64.Decoder decoder = Base64.getDecoder();
-        //                src   pos, len  expected
-        int[][] tests = { { 6,    3,   3,   3},   // xxx xxx    -> yyyy yyyy
-                          { 6,    3,   4,   3},
-                          { 6,    3,   5,   3},
-                          { 6,    3,   6,   6},
-                          { 6,   11,   4,   3},
-                          { 6,   11,   4,   3},
-                          { 6,   11,   5,   3},
-                          { 6,   11,   6,   6},
-                          { 7,    3,   6,   6},   // xxx xxx x  -> yyyy yyyy yy==
-                          { 7,    3,   7,   7},
-                          { 7,   11,   6,   6},
-                          { 7,   11,   7,   7},
-                          { 8,    3,   6,   6},   // xxx xxx xx -> yyyy yyyy yyy=
-                          { 8,    3,   7,   6},
-                          { 8,    3,   8,   8},
-                          { 8,   13,   6,   6},
-                          { 8,   13,   7,   6},
-                          { 8,   13,   8,   8},
-
-        };
-        ByteBuffer dstBuf = ByteBuffer.allocate(100);
-        for (boolean direct : new boolean[] { false, true}) {
-            for (int[] test : tests) {
-                byte[] src = new byte[test[0]];
-                rnd.nextBytes(src);
-                ByteBuffer srcBuf = direct ? ByteBuffer.allocate(100)
-                                           : ByteBuffer.allocateDirect(100);
-                srcBuf.put(encoder.encode(src)).flip();
-                dstBuf.clear().position(test[1]).limit(test[1]+ test[2]);
-                int ret = decoder.decode(srcBuf, dstBuf);
-                if (ret != test[3]) {
-                    System.out.printf(" [%6s] src=%d, pos=%d, len=%d, expected=%d, ret=%d%n",
-                                      direct?"direct":"",
-                                      test[0], test[1], test[2], test[3], ret);
-                    throw new RuntimeException("ret != expected");
-                }
-            }
-        }
-    }
-
     private static final void testEncode(Base64.Encoder enc, ByteBuffer bin, byte[] expected)
         throws Throwable {
 
@@ -587,71 +489,6 @@
         checkEqual(buf, expected, "Base64 dec.decode(bf) failed!");
     }
 
-    private static final void testEncode(Base64.Encoder enc, byte[] expected,
-                                         ByteBuffer ibb, ByteBuffer obb)
-        throws Throwable {
-        Random rnd = new Random();
-        int bytesOut = enc.encode(ibb, obb, 0);
-        if (ibb.hasRemaining()) {
-            throw new RuntimeException(
-                "Base64 enc.encode(bf, bf) failed with wrong return!");
-        }
-        obb.flip();
-        byte[] buf = new byte[obb.remaining()];
-        obb.get(buf);
-        checkEqual(buf, expected, "Base64 enc.encode(bf, bf) failed!");
-        ibb.rewind();
-        obb.position(0);
-        obb.limit(0);
-        bytesOut = 0;
-
-        do {  // increase the "limit" incrementally & randomly
-            int n = rnd.nextInt(expected.length - obb.position());
-            if (n == 0)
-                n = 1;
-            obb.limit(obb.limit() + n);
-            //obb.limit(Math.min(obb.limit() + n, expected.length));
-            bytesOut = enc.encode(ibb, obb, bytesOut);
-        } while (ibb.hasRemaining());
-        obb.flip();
-        buf = new byte[obb.remaining()];
-        obb.get(buf);
-        checkEqual(buf, expected, "Base64 enc.encode(bf, bf) failed!");
-    }
-
-    private static final void testDecode(Base64.Decoder dec, byte[] expected,
-                                         ByteBuffer ibb, ByteBuffer obb)
-        throws Throwable {
-        Random rnd = new Random();
-
-        dec.decode(ibb, obb);
-        if (ibb.hasRemaining()) {
-            throw new RuntimeException(
-                "Base64 dec.decode(bf, bf) failed with un-decoded ibb!");
-        }
-        obb.flip();
-        byte[] buf = new byte[obb.remaining()];
-        obb.get(buf);
-        checkEqual(buf, expected, "Base64 dec.decode(bf, bf) failed!");
-
-        ibb.rewind();
-        obb.position(0);
-        obb.limit(0);
-        do {  // increase the "limit" incrementally & randomly
-            int n = rnd.nextInt(expected.length - obb.position());
-            if (n == 0)
-                n = 1;
-            obb.limit(obb.limit() + n);
-            dec.decode(ibb, obb);
-         } while (ibb.hasRemaining());
-
-
-        obb.flip();
-        buf = new byte[obb.remaining()];
-        obb.get(buf);
-        checkEqual(buf, expected, "Base64 dec.decode(bf, bf) failed!");
-    }
-
     private static final void checkEqual(int v1, int v2, String msg)
         throws Throwable {
        if (v1 != v2) {
--- a/test/java/util/Base64/TestBase64Golden.java	Tue Dec 03 17:25:28 2013 -0800
+++ b/test/java/util/Base64/TestBase64Golden.java	Tue Dec 03 17:44:31 2013 -0800
@@ -55,7 +55,6 @@
         test0(Base64Type.MIME, Base64.getMimeEncoder(), Base64.getMimeDecoder(),
               "plain.txt", "mimeEncode.txt");
         test1();
-        test2();
     }
 
     public static void test0(Base64Type type, Encoder encoder, Decoder decoder,
@@ -113,28 +112,6 @@
             assertEqual(resBuf, encodedBuf);
             srcBuf.rewind(); // reset for next test
 
-            // test encode(ByteBuffer, ByteBuffer, bytesOut)
-            resBuf.clear();
-            len = encoder.encode(srcBuf, resBuf, 0);
-            assertEqual(len, encodedArr.length);
-            assertEqual(srcBuf.position(), limit);
-            assertEqual(srcBuf.limit(), limit);
-            assertEqual(resBuf.position(), len);
-            resBuf.flip();
-            assertEqual(resBuf, encodedBuf);
-            srcBuf.rewind();
-
-            // test encode(ByteBuffer, ByteBuffer, bytesOut)[direct]
-            ByteBuffer resBuf_d = ByteBuffer.allocateDirect(encodedArr.length);
-            len = encoder.encode(srcBuf, resBuf_d, 0);
-            assertEqual(len, encodedArr.length);
-            assertEqual(srcBuf.position(), limit);
-            assertEqual(srcBuf.limit(), limit);
-            assertEqual(resBuf_d.position(), len);
-            resBuf_d.flip();
-            assertEqual(resBuf_d, encodedBuf);
-            srcBuf.rewind();
-
             // test String encodeToString(byte[])
             String resEncodeStr = encoder.encodeToString(srcArr);
             assertEqual(resEncodeStr, encodedStr);
@@ -157,28 +134,6 @@
             assertEqual(resBuf, srcBuf);
             encodedBuf.rewind(); // reset for next test
 
-            // test int decode(ByteBuffer, ByteBuffer)
-            resBuf.clear();
-            len = decoder.decode(encodedBuf, resBuf);
-            assertEqual(len, srcArr.length);
-            assertEqual(encodedBuf.position(), limit);
-            assertEqual(encodedBuf.limit(), limit);
-            assertEqual(resBuf.position(), len);
-            resBuf.flip();
-            assertEqual(resBuf, srcBuf);
-            encodedBuf.rewind(); // reset for next test
-
-            // test int decode(ByteBuffer, ByteBuffer)[direct]
-            resBuf_d = ByteBuffer.allocateDirect(srcArr.length);
-            len = decoder.decode(encodedBuf, resBuf_d);
-            assertEqual(len, srcArr.length);
-            assertEqual(encodedBuf.position(), limit);
-            assertEqual(encodedBuf.limit(), limit);
-            assertEqual(resBuf_d.position(), len);
-            resBuf_d.flip();
-            assertEqual(resBuf_d, srcBuf);
-            encodedBuf.rewind(); // reset for next test
-
             // test byte[] decode(String)
             resArr = decoder.decode(encodedStr);
             assertEqual(resArr, srcArr);
@@ -197,35 +152,6 @@
     }
 
     private static void test1() throws Exception {
-        byte[] src = new byte[6];
-        new Random().nextBytes(src);
-
-        ByteBuffer srcBuf = ByteBuffer.allocate(10);
-        srcBuf.position(2);
-        srcBuf.mark();
-        srcBuf.limit(8);
-        srcBuf.put(src);
-        srcBuf.reset();
-
-        ByteBuffer dstBuf = ByteBuffer.allocate((src.length + 2) / 3 * 4);
-        Base64.getEncoder().encode(srcBuf, dstBuf, 0);
-        dstBuf.rewind();
-        byte[] dst = new byte[dstBuf.limit()];
-        dstBuf.get(dst);
-        System.out.printf("%n    src[%d]: %s%n", src.length, new String(src));
-        System.out.printf("encoded[%d]: %s%n",   dst.length, new String(dst));
-        assertEqual(src, Base64.getDecoder().decode(dst));
-
-        dstBuf = ByteBuffer.allocateDirect((src.length + 2) / 3 * 4);
-        srcBuf.reset();
-        Base64.getEncoder().encode(srcBuf, dstBuf, 0);
-        dstBuf.rewind();
-        dst = new byte[dstBuf.limit()];
-        dstBuf.get(dst);
-        assertEqual(src, Base64.getDecoder().decode(dst));
-    }
-
-    private static void test2() throws Exception {
         byte[] src = new byte[] {
             46, -97, -35, -44, 127, -60, -39, -4, -112, 34, -57, 47, -14, 67,
             40, 18, 90, -59, 68, 112, 23, 121, -91, 94, 35, 49, 104, 17, 30,