# HG changeset patch # User sherman # Date 1386121471 28800 # Node ID 301d76b8cb552fb9ca31efade1540d5f652479cb # Parent 75165f6c1c505ff43f7fd235a95b2e7955413b78 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 diff -r 75165f6c1c50 -r 301d76b8cb55 src/share/classes/java/util/Base64.java --- 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. - * - *
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. - * - *
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. - * - *
Recommended Usage Example - *
- * 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(); - * ... - * } - *- * - * @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). - *
- * For decoders that use the Basic and - * URL and Filename safe 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. - *
- * Decoders that use the MIME 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. * *
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 * + *
{@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. - * - *
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. - * - *
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. - * - *
The decoding operation will end and return if all remaining - * bytes in the input buffer have been decoded and written to the - * output buffer. - * - *
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. - * - *
Recommended Usage Example - *
- * 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(); - * ... - * } - *- * - * @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. * *
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; diff -r 75165f6c1c50 -r 301d76b8cb55 test/java/util/Base64/Base64GetEncoderTest.java --- 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) diff -r 75165f6c1c50 -r 301d76b8cb55 test/java/util/Base64/TestBase64.java --- 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) { diff -r 75165f6c1c50 -r 301d76b8cb55 test/java/util/Base64/TestBase64Golden.java --- 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,