src/share/classes/java/util/Base64.java

Print this page

        

@@ -48,11 +48,11 @@
  *     character. The decoder rejects data that contains characters
  *     outside the base64 alphabet.</p></li>
  *
  * <li><a name="url"><b>URL and Filename safe</b></a>
  * <p> Uses the "URL and Filename safe Base64 Alphabet" as specified
- *     in Table 2 of RFC 4648 for encoding and decoding. The
+*     in Table 2 of RFC 4648 for encoding and decoding. The
  *     encoder does not add any line feed (line separator) character.
  *     The decoder rejects data that contains characters outside the
  *     base64 alphabet.</p></li>
  *
  * <li><a name="mime"><b>MIME</b></a>

@@ -136,11 +136,11 @@
                      "Illegal base64 line separator character 0x" + Integer.toString(b, 16));
          }
          if (lineLength <= 0) {
              return Encoder.RFC4648;
          }
-         return new Encoder(false, lineSeparator, lineLength >> 2 << 2);
+         return new Encoder(false, lineSeparator, lineLength >> 2 << 2, true);
     }
 
     /**
      * Returns a {@link Decoder} that decodes using the
      * <a href="#basic">Basic</a> type base64 encoding scheme.

@@ -190,15 +190,17 @@
     public static class Encoder {
 
         private final byte[] newline;
         private final int linemax;
         private final boolean isURL;
+        private final boolean doPadding;
 
-        private Encoder(boolean isURL, byte[] newline, int linemax) {
+        private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) {
             this.isURL = isURL;
             this.newline = newline;
             this.linemax = linemax;
+            this.doPadding = doPadding;
         }
 
         /**
          * This array is a lookup table that translates 6-bit positive integer
          * index values into their "Base64 Alphabet" equivalents as specified

@@ -226,13 +228,26 @@
         };
 
         private static final int MIMELINEMAX = 76;
         private static final byte[] CRLF = new byte[] {'\r', '\n'};
 
-        static final Encoder RFC4648 = new Encoder(false, null, -1);
-        static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1);
-        static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX);
+        static final Encoder RFC4648 = new Encoder(false, null, -1, true);
+        static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
+        static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
+
+        private final int outLength(int srclen) {
+            int len = 0;
+            if (doPadding) {
+                len = 4 * ((srclen + 2) / 3);
+            } else {
+                int n = srclen % 3;
+                len = 4 * (srclen / 3) + (n == 0 ? 0 : n + 1);
+            }
+            if (linemax > 0)                                  // line separators
+                len += (len - 1) / linemax * newline.length;
+            return len;
+        }
 
         /**
          * Encodes all bytes from the specified byte array into a newly-allocated
          * byte array using the {@link Base64} encoding scheme. The returned byte
          * array is of the length of the resulting bytes.

@@ -241,13 +256,11 @@
          *          the byte array to encode
          * @return  A newly-allocated byte array containing the resulting
          *          encoded bytes.
          */
         public byte[] encode(byte[] src) {
-            int len = 4 * ((src.length + 2) / 3);    // dst array size
-            if (linemax > 0)                          // line separators
-                len += (len - 1) / linemax * newline.length;
+            int len = outLength(src.length);          // dst array size
             byte[] dst = new byte[len];
             int ret = encode0(src, 0, src.length, dst);
             if (ret != dst.length)
                  return Arrays.copyOf(dst, ret);
             return dst;

@@ -271,14 +284,11 @@
          *
          * @throws  IllegalArgumentException if {@code dst} does not have enough
          *          space for encoding all input bytes.
          */
         public int encode(byte[] src, byte[] dst) {
-            int len = 4 * ((src.length + 2) / 3);    // dst array size
-            if (linemax > 0) {
-                len += (len - 1) / linemax * newline.length;
-            }
+            int len = outLength(src.length);         // dst array size
             if (dst.length < len)
                 throw new IllegalArgumentException(
                     "Output byte array is too small for encoding all input bytes");
             return encode0(src, 0, src.length, dst);
         }

@@ -319,13 +329,11 @@
          * @param   buffer
          *          the source ByteBuffer to encode
          * @return  A newly-allocated byte buffer containing the encoded bytes.
          */
         public ByteBuffer encode(ByteBuffer buffer) {
-            int len = 4 * ((buffer.remaining() + 2) / 3);
-            if (linemax > 0)
-                len += (len - 1) / linemax * newline.length;
+            int len = outLength(buffer.remaining());
             byte[] dst = new byte[len];
             int ret = 0;
             if (buffer.hasArray()) {
                 ret = encode0(buffer.array(),
                               buffer.arrayOffset() + buffer.position(),

@@ -413,11 +421,29 @@
          *          specified Base64 encoded format
          */
         public OutputStream wrap(OutputStream os) {
             Objects.requireNonNull(os);
             return new EncOutputStream(os, isURL ? toBase64URL : toBase64,
-                                       newline, linemax);
+                                       newline, linemax, doPadding);
+        }
+
+        /**
+         * Returns an encoder instance that encodes equivalently to this one,
+         * but without adding any padding character at the end of the encoded
+         * byte data.
+         *
+         * <p> The encoding scheme of this encoder instance is unaffected by
+         * this invocation. The returned encoder instance should be used for
+         * non-padding encoding operation.
+         *
+         * @return an equivalent encoder that encodes without adding any
+         *         padding character at the end
+         */
+        public Encoder withoutPadding() {
+            if (!doPadding)
+                return this;
+            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();

@@ -474,19 +500,23 @@
                 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());
             }

@@ -546,19 +576,23 @@
                 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);
             }

@@ -595,19 +629,23 @@
             if (sp < end) {               // 1 or 2 leftover bytes
                 int b0 = src[sp++] & 0xff;
                 dst[dp++] = (byte)base64[b0 >> 2];
                 if (sp == end) {
                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
+                    if (doPadding) {
                     dst[dp++] = '=';
                     dst[dp++] = '=';
+                    }
                 } else {
                     int b1 = src[sp++] & 0xff;
                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
                     dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
+                    if (doPadding) {
                     dst[dp++] = '=';
                 }
             }
+            }
             return dp;
         }
     }
 
     /**

@@ -1147,18 +1185,20 @@
         private boolean closed = false;
 
         private final char[] base64;    // byte->base64 mapping
         private final byte[] newline;   // line separator, if needed
         private final int linemax;
+        private final boolean doPadding;// whether or not to pad
         private int linepos = 0;
 
-        EncOutputStream(OutputStream os,
-                        char[] base64, byte[] newline, int linemax) {
+        EncOutputStream(OutputStream os, char[] base64,
+                        byte[] newline, int linemax, boolean doPadding) {
             super(os);
             this.base64 = base64;
             this.newline = newline;
             this.linemax = linemax;
+            this.doPadding = doPadding;
         }
 
         @Override
         public void write(int b) throws IOException {
             byte[] buf = new byte[1];

@@ -1226,19 +1266,23 @@
                 closed = true;
                 if (leftover == 1) {
                     checkNewline();
                     out.write(base64[b0 >> 2]);
                     out.write(base64[(b0 << 4) & 0x3f]);
+                    if (doPadding) {
                     out.write('=');
                     out.write('=');
+                    }
                 } else if (leftover == 2) {
                     checkNewline();
                     out.write(base64[b0 >> 2]);
                     out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
                     out.write(base64[(b1 << 2) & 0x3f]);
+                    if (doPadding) {
                     out.write('=');
                 }
+                }
                 leftover = 0;
                 out.close();
             }
         }
     }