--- a/src/java.base/share/classes/java/util/Base64.java Wed Feb 14 19:03:12 2018 +0100
+++ b/src/java.base/share/classes/java/util/Base64.java Wed Feb 14 14:13:42 2018 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -693,6 +693,25 @@
int bits = 0;
int shiftto = 18; // pos of first byte of 4-byte atom
while (sp < sl) {
+ if (bits == 0 && sp + 4 < sl) { // fast path
+ int sl0 = sp + ((sl - sp) & ~0b11);
+ while (sp < sl0) {
+ int b1 = base64[src[sp++] & 0xff];
+ int b2 = base64[src[sp++] & 0xff];
+ int b3 = base64[src[sp++] & 0xff];
+ int b4 = base64[src[sp++] & 0xff];
+ if ((b1 | b2 | b3 | b4) < 0) { // non base64 byte
+ sp -= 4;
+ break;
+ }
+ int bits0 = b1 << 18 | b2 << 12 | b3 << 6 | b4;
+ dst[dp++] = (byte)(bits0 >> 16);
+ dst[dp++] = (byte)(bits0 >> 8);
+ dst[dp++] = (byte)(bits0);
+ }
+ if (sp >= sl)
+ break;
+ }
int b = src[sp++] & 0xff;
if ((b = base64[b]) < 0) {
if (b == -2) { // padding byte '='
@@ -762,6 +781,7 @@
private final int linemax;
private final boolean doPadding;// whether or not to pad
private int linepos = 0;
+ private byte[] buf;
EncOutputStream(OutputStream os, char[] base64,
byte[] newline, int linemax, boolean doPadding) {
@@ -770,6 +790,7 @@
this.newline = newline;
this.linemax = linemax;
this.doPadding = doPadding;
+ this.buf = new byte[linemax <= 0 ? 8124 : linemax];
}
@Override
@@ -786,6 +807,14 @@
}
}
+ private void writeb4(char b1, char b2, char b3, char b4) throws IOException {
+ buf[0] = (byte)b1;
+ buf[1] = (byte)b2;
+ buf[2] = (byte)b3;
+ buf[3] = (byte)b4;
+ out.write(buf, 0, 4);
+ }
+
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (closed)
@@ -806,25 +835,34 @@
b2 = b[off++] & 0xff;
len--;
checkNewline();
- out.write(base64[b0 >> 2]);
- out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
- out.write(base64[(b1 << 2) & 0x3f | (b2 >> 6)]);
- out.write(base64[b2 & 0x3f]);
+ writeb4(base64[b0 >> 2],
+ base64[(b0 << 4) & 0x3f | (b1 >> 4)],
+ base64[(b1 << 2) & 0x3f | (b2 >> 6)],
+ base64[b2 & 0x3f]);
linepos += 4;
}
int nBits24 = len / 3;
leftover = len - (nBits24 * 3);
- while (nBits24-- > 0) {
+
+ while (nBits24 > 0) {
checkNewline();
- int bits = (b[off++] & 0xff) << 16 |
- (b[off++] & 0xff) << 8 |
- (b[off++] & 0xff);
- out.write(base64[(bits >>> 18) & 0x3f]);
- out.write(base64[(bits >>> 12) & 0x3f]);
- out.write(base64[(bits >>> 6) & 0x3f]);
- out.write(base64[bits & 0x3f]);
- linepos += 4;
- }
+ int dl = linemax <= 0 ? buf.length : buf.length - linepos;
+ int sl = off + Math.min(nBits24, dl / 4) * 3;
+ int dp = 0;
+ for (int sp = off; sp < sl; ) {
+ int bits = (b[sp++] & 0xff) << 16 |
+ (b[sp++] & 0xff) << 8 |
+ (b[sp++] & 0xff);
+ buf[dp++] = (byte)base64[(bits >>> 18) & 0x3f];
+ buf[dp++] = (byte)base64[(bits >>> 12) & 0x3f];
+ buf[dp++] = (byte)base64[(bits >>> 6) & 0x3f];
+ buf[dp++] = (byte)base64[bits & 0x3f];
+ }
+ out.write(buf, 0, dp);
+ off = sl;
+ linepos += dp;
+ nBits24 -= dp / 4;
+ }
if (leftover == 1) {
b0 = b[off++] & 0xff;
} else if (leftover == 2) {
@@ -889,6 +927,52 @@
return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff;
}
+ private int eof(byte[] b, int off, int len, int oldOff)
+ throws IOException
+ {
+ eof = true;
+ if (nextin != 18) {
+ if (nextin == 12)
+ throw new IOException("Base64 stream has one un-decoded dangling byte.");
+ // treat ending xx/xxx without padding character legal.
+ // same logic as v == '=' below
+ b[off++] = (byte)(bits >> (16));
+ if (nextin == 0) { // only one padding byte
+ if (len == 1) { // no enough output space
+ bits >>= 8; // shift to lowest byte
+ nextout = 0;
+ } else {
+ b[off++] = (byte) (bits >> 8);
+ }
+ }
+ }
+ return off == oldOff ? -1 : off - oldOff;
+ }
+
+ private int padding(byte[] b, int off, int len, int oldOff)
+ throws IOException
+ {
+ // = shiftto==18 unnecessary padding
+ // x= shiftto==12 dangling x, invalid unit
+ // xx= shiftto==6 && missing last '='
+ // xx=y or last is not '='
+ if (nextin == 18 || nextin == 12 ||
+ nextin == 6 && is.read() != '=') {
+ throw new IOException("Illegal base64 ending sequence:" + nextin);
+ }
+ b[off++] = (byte)(bits >> (16));
+ if (nextin == 0) { // only one padding byte
+ if (len == 1) { // no enough output space
+ bits >>= 8; // shift to lowest byte
+ nextout = 0;
+ } else {
+ b[off++] = (byte) (bits >> 8);
+ }
+ }
+ eof = true;
+ return off - oldOff;
+ }
+
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (closed)
@@ -898,82 +982,46 @@
if (off < 0 || len < 0 || len > b.length - off)
throw new IndexOutOfBoundsException();
int oldOff = off;
- if (nextout >= 0) { // leftover output byte(s) in bits buf
- do {
- if (len == 0)
- return off - oldOff;
- b[off++] = (byte)(bits >> nextout);
- len--;
- nextout -= 8;
- } while (nextout >= 0);
- bits = 0;
+ while (nextout >= 0) { // leftover output byte(s) in bits buf
+ if (len == 0)
+ return off - oldOff;
+ b[off++] = (byte)(bits >> nextout);
+ len--;
+ nextout -= 8;
}
+ bits = 0;
while (len > 0) {
int v = is.read();
if (v == -1) {
- eof = true;
- if (nextin != 18) {
- if (nextin == 12)
- throw new IOException("Base64 stream has one un-decoded dangling byte.");
- // treat ending xx/xxx without padding character legal.
- // same logic as v == '=' below
- 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);
- }
- }
- }
- if (off == oldOff)
- return -1;
- else
- return off - oldOff;
+ return eof(b, off, len, oldOff);
}
- if (v == '=') { // padding byte(s)
- // = shiftto==18 unnecessary padding
- // x= shiftto==12 dangling x, invalid unit
- // xx= shiftto==6 && missing last '='
- // xx=y or last is not '='
- if (nextin == 18 || nextin == 12 ||
- nextin == 6 && is.read() != '=') {
- throw new IOException("Illegal base64 ending sequence:" + nextin);
+ if ((v = base64[v]) < 0) {
+ if (v == -2) { // padding byte(s)
+ return padding(b, off, len, oldOff);
}
- 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);
- }
+ if (v == -1) {
+ if (!isMIME)
+ throw new IOException("Illegal base64 character " +
+ Integer.toString(v, 16));
+ continue; // skip if for rfc2045
}
- eof = true;
- break;
- }
- if ((v = base64[v]) == -1) {
- if (isMIME) // skip if for rfc2045
- continue;
- else
- throw new IOException("Illegal base64 character " +
- Integer.toString(v, 16));
+ // neve be here
}
bits |= (v << nextin);
if (nextin == 0) {
- nextin = 18; // clear for next
- nextout = 16;
- while (nextout >= 0) {
- b[off++] = (byte)(bits >> nextout);
- len--;
- nextout -= 8;
- if (len == 0 && nextout >= 0) { // don't clean "bits"
- return off - oldOff;
- }
+ nextin = 18; // clear for next in
+ b[off++] = (byte)(bits >> 16);
+ if (len == 1) {
+ nextout = 8; // 2 bytes left in bits
+ break;
}
+ b[off++] = (byte)(bits >> 8);
+ if (len == 2) {
+ nextout = 0; // 1 byte left in bits
+ break;
+ }
+ b[off++] = (byte)bits;
+ len -= 3;
bits = 0;
} else {
nextin -= 6;