1 /*
2  * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.util;
27 
28 import java.io.FilterOutputStream;
29 import java.io.InputStream;
30 import java.io.IOException;
31 import java.io.OutputStream;
32 import java.nio.ByteBuffer;
33 import java.nio.charset.StandardCharsets;
34 
35 import jdk.internal.vm.annotation.IntrinsicCandidate;
36 
37 /**
38  * This class consists exclusively of static methods for obtaining
39  * encoders and decoders for the Base64 encoding scheme. The
40  * implementation of this class supports the following types of Base64
41  * as specified in
42  * <a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a> and
43  * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>.
44  *
45  * <ul>
46  * <li><a id="basic"><b>Basic</b></a>
47  * <p> Uses "The Base64 Alphabet" as specified in Table 1 of
48  *     RFC 4648 and RFC 2045 for encoding and decoding operation.
49  *     The encoder does not add any line feed (line separator)
50  *     character. The decoder rejects data that contains characters
51  *     outside the base64 alphabet.</p></li>
52  *
53  * <li><a id="url"><b>URL and Filename safe</b></a>
54  * <p> Uses the "URL and Filename safe Base64 Alphabet" as specified
55  *     in Table 2 of RFC 4648 for encoding and decoding. The
56  *     encoder does not add any line feed (line separator) character.
57  *     The decoder rejects data that contains characters outside the
58  *     base64 alphabet.</p></li>
59  *
60  * <li><a id="mime"><b>MIME</b></a>
61  * <p> Uses "The Base64 Alphabet" as specified in Table 1 of
62  *     RFC 2045 for encoding and decoding operation. The encoded output
63  *     must be represented in lines of no more than 76 characters each
64  *     and uses a carriage return {@code '\r'} followed immediately by
65  *     a linefeed {@code '\n'} as the line separator. No line separator
66  *     is added to the end of the encoded output. All line separators
67  *     or other characters not found in the base64 alphabet table are
68  *     ignored in decoding operation.</p></li>
69  * </ul>
70  *
71  * <p> Unless otherwise noted, passing a {@code null} argument to a
72  * method of this class will cause a {@link java.lang.NullPointerException
73  * NullPointerException} to be thrown.
74  *
75  * @author  Xueming Shen
76  * @since   1.8
77  */
78 
79 public class Base64 {
80 
Base64()81     private Base64() {}
82 
83     /**
84      * Returns a {@link Encoder} that encodes using the
85      * <a href="#basic">Basic</a> type base64 encoding scheme.
86      *
87      * @return  A Base64 encoder.
88      */
getEncoder()89     public static Encoder getEncoder() {
90          return Encoder.RFC4648;
91     }
92 
93     /**
94      * Returns a {@link Encoder} that encodes using the
95      * <a href="#url">URL and Filename safe</a> type base64
96      * encoding scheme.
97      *
98      * @return  A Base64 encoder.
99      */
getUrlEncoder()100     public static Encoder getUrlEncoder() {
101          return Encoder.RFC4648_URLSAFE;
102     }
103 
104     /**
105      * Returns a {@link Encoder} that encodes using the
106      * <a href="#mime">MIME</a> type base64 encoding scheme.
107      *
108      * @return  A Base64 encoder.
109      */
getMimeEncoder()110     public static Encoder getMimeEncoder() {
111         return Encoder.RFC2045;
112     }
113 
114     /**
115      * Returns a {@link Encoder} that encodes using the
116      * <a href="#mime">MIME</a> type base64 encoding scheme
117      * with specified line length and line separators.
118      *
119      * @param   lineLength
120      *          the length of each output line (rounded down to nearest multiple
121      *          of 4). If the rounded down line length is not a positive value,
122      *          the output will not be separated in lines
123      * @param   lineSeparator
124      *          the line separator for each output line
125      *
126      * @return  A Base64 encoder.
127      *
128      * @throws  IllegalArgumentException if {@code lineSeparator} includes any
129      *          character of "The Base64 Alphabet" as specified in Table 1 of
130      *          RFC 2045.
131      */
getMimeEncoder(int lineLength, byte[] lineSeparator)132     public static Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) {
133          Objects.requireNonNull(lineSeparator);
134          int[] base64 = Decoder.fromBase64;
135          for (byte b : lineSeparator) {
136              if (base64[b & 0xff] != -1)
137                  throw new IllegalArgumentException(
138                      "Illegal base64 line separator character 0x" + Integer.toString(b, 16));
139          }
140          // round down to nearest multiple of 4
141          lineLength &= ~0b11;
142          if (lineLength <= 0) {
143              return Encoder.RFC4648;
144          }
145          return new Encoder(false, lineSeparator, lineLength, true);
146     }
147 
148     /**
149      * Returns a {@link Decoder} that decodes using the
150      * <a href="#basic">Basic</a> type base64 encoding scheme.
151      *
152      * @return  A Base64 decoder.
153      */
getDecoder()154     public static Decoder getDecoder() {
155          return Decoder.RFC4648;
156     }
157 
158     /**
159      * Returns a {@link Decoder} that decodes using the
160      * <a href="#url">URL and Filename safe</a> type base64
161      * encoding scheme.
162      *
163      * @return  A Base64 decoder.
164      */
getUrlDecoder()165     public static Decoder getUrlDecoder() {
166          return Decoder.RFC4648_URLSAFE;
167     }
168 
169     /**
170      * Returns a {@link Decoder} that decodes using the
171      * <a href="#mime">MIME</a> type base64 decoding scheme.
172      *
173      * @return  A Base64 decoder.
174      */
getMimeDecoder()175     public static Decoder getMimeDecoder() {
176          return Decoder.RFC2045;
177     }
178 
179     /**
180      * This class implements an encoder for encoding byte data using
181      * the Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
182      *
183      * <p> Instances of {@link Encoder} class are safe for use by
184      * multiple concurrent threads.
185      *
186      * <p> Unless otherwise noted, passing a {@code null} argument to
187      * a method of this class will cause a
188      * {@link java.lang.NullPointerException NullPointerException} to
189      * be thrown.
190      * <p> If the encoded byte output of the needed size can not
191      *     be allocated, the encode methods of this class will
192      *     cause an {@link java.lang.OutOfMemoryError OutOfMemoryError}
193      *     to be thrown.
194      *
195      * @see     Decoder
196      * @since   1.8
197      */
198     public static class Encoder {
199 
200         private final byte[] newline;
201         private final int linemax;
202         private final boolean isURL;
203         private final boolean doPadding;
204 
Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding)205         private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) {
206             this.isURL = isURL;
207             this.newline = newline;
208             this.linemax = linemax;
209             this.doPadding = doPadding;
210         }
211 
212         /**
213          * This array is a lookup table that translates 6-bit positive integer
214          * index values into their "Base64 Alphabet" equivalents as specified
215          * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
216          */
217         private static final char[] toBase64 = {
218             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
219             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
220             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
221             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
222             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
223         };
224 
225         /**
226          * It's the lookup table for "URL and Filename safe Base64" as specified
227          * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
228          * '_'. This table is used when BASE64_URL is specified.
229          */
230         private static final char[] toBase64URL = {
231             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
232             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
233             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
234             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
235             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
236         };
237 
238         private static final int MIMELINEMAX = 76;
239         private static final byte[] CRLF = new byte[] {'\r', '\n'};
240 
241         static final Encoder RFC4648 = new Encoder(false, null, -1, true);
242         static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
243         static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
244 
245         /**
246          * Calculates the length of the encoded output bytes.
247          *
248          * @param srclen length of the bytes to encode
249          * @param throwOOME if true, throws OutOfMemoryError if the length of
250          *                  the encoded bytes overflows; else returns the
251          *                  length
252          * @return length of the encoded bytes, or -1 if the length overflows
253          *
254          */
encodedOutLength(int srclen, boolean throwOOME)255         private final int encodedOutLength(int srclen, boolean throwOOME) {
256             int len = 0;
257             try {
258                 if (doPadding) {
259                     len = Math.multiplyExact(4, (Math.addExact(srclen, 2) / 3));
260                 } else {
261                     int n = srclen % 3;
262                     len = Math.addExact(Math.multiplyExact(4, (srclen / 3)), (n == 0 ? 0 : n + 1));
263                 }
264                 if (linemax > 0) {                             // line separators
265                     len = Math.addExact(len, (len - 1) / linemax * newline.length);
266                 }
267             } catch (ArithmeticException ex) {
268                 if (throwOOME) {
269                     throw new OutOfMemoryError("Encoded size is too large");
270                 } else {
271                     // let the caller know that encoded bytes length
272                     // is too large
273                     len = -1;
274                 }
275             }
276             return len;
277         }
278 
279         /**
280          * Encodes all bytes from the specified byte array into a newly-allocated
281          * byte array using the {@link Base64} encoding scheme. The returned byte
282          * array is of the length of the resulting bytes.
283          *
284          * @param   src
285          *          the byte array to encode
286          * @return  A newly-allocated byte array containing the resulting
287          *          encoded bytes.
288          */
encode(byte[] src)289         public byte[] encode(byte[] src) {
290             int len = encodedOutLength(src.length, true);          // dst array size
291             byte[] dst = new byte[len];
292             int ret = encode0(src, 0, src.length, dst);
293             if (ret != dst.length)
294                  return Arrays.copyOf(dst, ret);
295             return dst;
296         }
297 
298         /**
299          * Encodes all bytes from the specified byte array using the
300          * {@link Base64} encoding scheme, writing the resulting bytes to the
301          * given output byte array, starting at offset 0.
302          *
303          * <p> It is the responsibility of the invoker of this method to make
304          * sure the output byte array {@code dst} has enough space for encoding
305          * all bytes from the input byte array. No bytes will be written to the
306          * output byte array if the output byte array is not big enough.
307          *
308          * @param   src
309          *          the byte array to encode
310          * @param   dst
311          *          the output byte array
312          * @return  The number of bytes written to the output byte array
313          *
314          * @throws  IllegalArgumentException if {@code dst} does not have enough
315          *          space for encoding all input bytes.
316          */
encode(byte[] src, byte[] dst)317         public int encode(byte[] src, byte[] dst) {
318             int len = encodedOutLength(src.length, false);         // dst array size
319             if (dst.length < len || len == -1)
320                 throw new IllegalArgumentException(
321                     "Output byte array is too small for encoding all input bytes");
322             return encode0(src, 0, src.length, dst);
323         }
324 
325         /**
326          * Encodes the specified byte array into a String using the {@link Base64}
327          * encoding scheme.
328          *
329          * <p> This method first encodes all input bytes into a base64 encoded
330          * byte array and then constructs a new String by using the encoded byte
331          * array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1
332          * ISO-8859-1} charset.
333          *
334          * <p> In other words, an invocation of this method has exactly the same
335          * effect as invoking
336          * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.
337          *
338          * @param   src
339          *          the byte array to encode
340          * @return  A String containing the resulting Base64 encoded characters
341          */
342         @SuppressWarnings("deprecation")
encodeToString(byte[] src)343         public String encodeToString(byte[] src) {
344             byte[] encoded = encode(src);
345             return new String(encoded, 0, 0, encoded.length);
346         }
347 
348         /**
349          * Encodes all remaining bytes from the specified byte buffer into
350          * a newly-allocated ByteBuffer using the {@link Base64} encoding
351          * scheme.
352          *
353          * Upon return, the source buffer's position will be updated to
354          * its limit; its limit will not have been changed. The returned
355          * output buffer's position will be zero and its limit will be the
356          * number of resulting encoded bytes.
357          *
358          * @param   buffer
359          *          the source ByteBuffer to encode
360          * @return  A newly-allocated byte buffer containing the encoded bytes.
361          */
encode(ByteBuffer buffer)362         public ByteBuffer encode(ByteBuffer buffer) {
363             int len = encodedOutLength(buffer.remaining(), true);
364             byte[] dst = new byte[len];
365             int ret = 0;
366             if (buffer.hasArray()) {
367                 ret = encode0(buffer.array(),
368                               buffer.arrayOffset() + buffer.position(),
369                               buffer.arrayOffset() + buffer.limit(),
370                               dst);
371                 buffer.position(buffer.limit());
372             } else {
373                 byte[] src = new byte[buffer.remaining()];
374                 buffer.get(src);
375                 ret = encode0(src, 0, src.length, dst);
376             }
377             if (ret != dst.length)
378                  dst = Arrays.copyOf(dst, ret);
379             return ByteBuffer.wrap(dst);
380         }
381 
382         /**
383          * Wraps an output stream for encoding byte data using the {@link Base64}
384          * encoding scheme.
385          *
386          * <p> It is recommended to promptly close the returned output stream after
387          * use, during which it will flush all possible leftover bytes to the underlying
388          * output stream. Closing the returned output stream will close the underlying
389          * output stream.
390          *
391          * @param   os
392          *          the output stream.
393          * @return  the output stream for encoding the byte data into the
394          *          specified Base64 encoded format
395          */
wrap(OutputStream os)396         public OutputStream wrap(OutputStream os) {
397             Objects.requireNonNull(os);
398             return new EncOutputStream(os, isURL ? toBase64URL : toBase64,
399                                        newline, linemax, doPadding);
400         }
401 
402         /**
403          * Returns an encoder instance that encodes equivalently to this one,
404          * but without adding any padding character at the end of the encoded
405          * byte data.
406          *
407          * <p> The encoding scheme of this encoder instance is unaffected by
408          * this invocation. The returned encoder instance should be used for
409          * non-padding encoding operation.
410          *
411          * @return an equivalent encoder that encodes without adding any
412          *         padding character at the end
413          */
withoutPadding()414         public Encoder withoutPadding() {
415             if (!doPadding)
416                 return this;
417             return new Encoder(isURL, newline, linemax, false);
418         }
419 
420         @IntrinsicCandidate
encodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL)421         private void encodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL) {
422             char[] base64 = isURL ? toBase64URL : toBase64;
423             for (int sp0 = sp, dp0 = dp ; sp0 < sl; ) {
424                 int bits = (src[sp0++] & 0xff) << 16 |
425                            (src[sp0++] & 0xff) <<  8 |
426                            (src[sp0++] & 0xff);
427                 dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
428                 dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
429                 dst[dp0++] = (byte)base64[(bits >>> 6)  & 0x3f];
430                 dst[dp0++] = (byte)base64[bits & 0x3f];
431             }
432         }
433 
encode0(byte[] src, int off, int end, byte[] dst)434         private int encode0(byte[] src, int off, int end, byte[] dst) {
435             char[] base64 = isURL ? toBase64URL : toBase64;
436             int sp = off;
437             int slen = (end - off) / 3 * 3;
438             int sl = off + slen;
439             if (linemax > 0 && slen  > linemax / 4 * 3)
440                 slen = linemax / 4 * 3;
441             int dp = 0;
442             while (sp < sl) {
443                 int sl0 = Math.min(sp + slen, sl);
444                 encodeBlock(src, sp, sl0, dst, dp, isURL);
445                 int dlen = (sl0 - sp) / 3 * 4;
446                 dp += dlen;
447                 sp = sl0;
448                 if (dlen == linemax && sp < end) {
449                     for (byte b : newline){
450                         dst[dp++] = b;
451                     }
452                 }
453             }
454             if (sp < end) {               // 1 or 2 leftover bytes
455                 int b0 = src[sp++] & 0xff;
456                 dst[dp++] = (byte)base64[b0 >> 2];
457                 if (sp == end) {
458                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
459                     if (doPadding) {
460                         dst[dp++] = '=';
461                         dst[dp++] = '=';
462                     }
463                 } else {
464                     int b1 = src[sp++] & 0xff;
465                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
466                     dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
467                     if (doPadding) {
468                         dst[dp++] = '=';
469                     }
470                 }
471             }
472             return dp;
473         }
474     }
475 
476     /**
477      * This class implements a decoder for decoding byte data using the
478      * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
479      *
480      * <p> The Base64 padding character {@code '='} is accepted and
481      * interpreted as the end of the encoded byte data, but is not
482      * required. So if the final unit of the encoded byte data only has
483      * two or three Base64 characters (without the corresponding padding
484      * character(s) padded), they are decoded as if followed by padding
485      * character(s). If there is a padding character present in the
486      * final unit, the correct number of padding character(s) must be
487      * present, otherwise {@code IllegalArgumentException} (
488      * {@code IOException} when reading from a Base64 stream) is thrown
489      * during decoding.
490      *
491      * <p> Instances of {@link Decoder} class are safe for use by
492      * multiple concurrent threads.
493      *
494      * <p> Unless otherwise noted, passing a {@code null} argument to
495      * a method of this class will cause a
496      * {@link java.lang.NullPointerException NullPointerException} to
497      * be thrown.
498      * <p> If the decoded byte output of the needed size can not
499      *     be allocated, the decode methods of this class will
500      *     cause an {@link java.lang.OutOfMemoryError OutOfMemoryError}
501      *     to be thrown.
502      *
503      * @see     Encoder
504      * @since   1.8
505      */
506     public static class Decoder {
507 
508         private final boolean isURL;
509         private final boolean isMIME;
510 
Decoder(boolean isURL, boolean isMIME)511         private Decoder(boolean isURL, boolean isMIME) {
512             this.isURL = isURL;
513             this.isMIME = isMIME;
514         }
515 
516         /**
517          * Lookup table for decoding unicode characters drawn from the
518          * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into
519          * their 6-bit positive integer equivalents.  Characters that
520          * are not in the Base64 alphabet but fall within the bounds of
521          * the array are encoded to -1.
522          *
523          */
524         private static final int[] fromBase64 = new int[256];
525         static {
Arrays.fill(fromBase64, -1)526             Arrays.fill(fromBase64, -1);
527             for (int i = 0; i < Encoder.toBase64.length; i++)
528                 fromBase64[Encoder.toBase64[i]] = i;
529             fromBase64['='] = -2;
530         }
531 
532         /**
533          * Lookup table for decoding "URL and Filename safe Base64 Alphabet"
534          * as specified in Table2 of the RFC 4648.
535          */
536         private static final int[] fromBase64URL = new int[256];
537 
538         static {
Arrays.fill(fromBase64URL, -1)539             Arrays.fill(fromBase64URL, -1);
540             for (int i = 0; i < Encoder.toBase64URL.length; i++)
541                 fromBase64URL[Encoder.toBase64URL[i]] = i;
542             fromBase64URL['='] = -2;
543         }
544 
545         static final Decoder RFC4648         = new Decoder(false, false);
546         static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
547         static final Decoder RFC2045         = new Decoder(false, true);
548 
549         /**
550          * Decodes all bytes from the input byte array using the {@link Base64}
551          * encoding scheme, writing the results into a newly-allocated output
552          * byte array. The returned byte array is of the length of the resulting
553          * bytes.
554          *
555          * @param   src
556          *          the byte array to decode
557          *
558          * @return  A newly-allocated byte array containing the decoded bytes.
559          *
560          * @throws  IllegalArgumentException
561          *          if {@code src} is not in valid Base64 scheme
562          */
decode(byte[] src)563         public byte[] decode(byte[] src) {
564             byte[] dst = new byte[decodedOutLength(src, 0, src.length)];
565             int ret = decode0(src, 0, src.length, dst);
566             if (ret != dst.length) {
567                 dst = Arrays.copyOf(dst, ret);
568             }
569             return dst;
570         }
571 
572         /**
573          * Decodes a Base64 encoded String into a newly-allocated byte array
574          * using the {@link Base64} encoding scheme.
575          *
576          * <p> An invocation of this method has exactly the same effect as invoking
577          * {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))}
578          *
579          * @param   src
580          *          the string to decode
581          *
582          * @return  A newly-allocated byte array containing the decoded bytes.
583          *
584          * @throws  IllegalArgumentException
585          *          if {@code src} is not in valid Base64 scheme
586          */
decode(String src)587         public byte[] decode(String src) {
588             // Android-changed: keep Java 8 implementation.
589             // return decode(src.getBytes(ISO_8859_1.INSTANCE));
590             return decode(src.getBytes(StandardCharsets.ISO_8859_1));
591         }
592 
593         /**
594          * Decodes all bytes from the input byte array using the {@link Base64}
595          * encoding scheme, writing the results into the given output byte array,
596          * starting at offset 0.
597          *
598          * <p> It is the responsibility of the invoker of this method to make
599          * sure the output byte array {@code dst} has enough space for decoding
600          * all bytes from the input byte array. No bytes will be written to
601          * the output byte array if the output byte array is not big enough.
602          *
603          * <p> If the input byte array is not in valid Base64 encoding scheme
604          * then some bytes may have been written to the output byte array before
605          * IllegalargumentException is thrown.
606          *
607          * @param   src
608          *          the byte array to decode
609          * @param   dst
610          *          the output byte array
611          *
612          * @return  The number of bytes written to the output byte array
613          *
614          * @throws  IllegalArgumentException
615          *          if {@code src} is not in valid Base64 scheme, or {@code dst}
616          *          does not have enough space for decoding all input bytes.
617          */
decode(byte[] src, byte[] dst)618         public int decode(byte[] src, byte[] dst) {
619             int len = decodedOutLength(src, 0, src.length);
620             if (dst.length < len || len == -1)
621                 throw new IllegalArgumentException(
622                     "Output byte array is too small for decoding all input bytes");
623             return decode0(src, 0, src.length, dst);
624         }
625 
626         /**
627          * Decodes all bytes from the input byte buffer using the {@link Base64}
628          * encoding scheme, writing the results into a newly-allocated ByteBuffer.
629          *
630          * <p> Upon return, the source buffer's position will be updated to
631          * its limit; its limit will not have been changed. The returned
632          * output buffer's position will be zero and its limit will be the
633          * number of resulting decoded bytes
634          *
635          * <p> {@code IllegalArgumentException} is thrown if the input buffer
636          * is not in valid Base64 encoding scheme. The position of the input
637          * buffer will not be advanced in this case.
638          *
639          * @param   buffer
640          *          the ByteBuffer to decode
641          *
642          * @return  A newly-allocated byte buffer containing the decoded bytes
643          *
644          * @throws  IllegalArgumentException
645          *          if {@code buffer} is not in valid Base64 scheme
646          */
decode(ByteBuffer buffer)647         public ByteBuffer decode(ByteBuffer buffer) {
648             int pos0 = buffer.position();
649             try {
650                 byte[] src;
651                 int sp, sl;
652                 if (buffer.hasArray()) {
653                     src = buffer.array();
654                     sp = buffer.arrayOffset() + buffer.position();
655                     sl = buffer.arrayOffset() + buffer.limit();
656                     buffer.position(buffer.limit());
657                 } else {
658                     src = new byte[buffer.remaining()];
659                     buffer.get(src);
660                     sp = 0;
661                     sl = src.length;
662                 }
663                 byte[] dst = new byte[decodedOutLength(src, sp, sl)];
664                 return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst));
665             } catch (IllegalArgumentException iae) {
666                 buffer.position(pos0);
667                 throw iae;
668             }
669         }
670 
671         /**
672          * Returns an input stream for decoding {@link Base64} encoded byte stream.
673          *
674          * <p> The {@code read}  methods of the returned {@code InputStream} will
675          * throw {@code IOException} when reading bytes that cannot be decoded.
676          *
677          * <p> Closing the returned input stream will close the underlying
678          * input stream.
679          *
680          * @param   is
681          *          the input stream
682          *
683          * @return  the input stream for decoding the specified Base64 encoded
684          *          byte stream
685          */
wrap(InputStream is)686         public InputStream wrap(InputStream is) {
687             Objects.requireNonNull(is);
688             return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
689         }
690 
691         /**
692          * Calculates the length of the decoded output bytes.
693          *
694          * @param src the byte array to decode
695          * @param sp the source  position
696          * @param sl the source limit
697          *
698          * @return length of the decoded bytes
699          *
700          */
decodedOutLength(byte[] src, int sp, int sl)701         private int decodedOutLength(byte[] src, int sp, int sl) {
702             int[] base64 = isURL ? fromBase64URL : fromBase64;
703             int paddings = 0;
704             int len = sl - sp;
705             if (len == 0)
706                 return 0;
707             if (len < 2) {
708                 if (isMIME && base64[0] == -1)
709                     return 0;
710                 throw new IllegalArgumentException(
711                     "Input byte[] should at least have 2 bytes for base64 bytes");
712             }
713             if (isMIME) {
714                 // scan all bytes to fill out all non-alphabet. a performance
715                 // trade-off of pre-scan or Arrays.copyOf
716                 int n = 0;
717                 while (sp < sl) {
718                     int b = src[sp++] & 0xff;
719                     if (b == '=') {
720                         len -= (sl - sp + 1);
721                         break;
722                     }
723                     if ((b = base64[b]) == -1)
724                         n++;
725                 }
726                 len -= n;
727             } else {
728                 if (src[sl - 1] == '=') {
729                     paddings++;
730                     if (src[sl - 2] == '=')
731                         paddings++;
732                 }
733             }
734             if (paddings == 0 && (len & 0x3) !=  0)
735                 paddings = 4 - (len & 0x3);
736 
737             // If len is near to Integer.MAX_VALUE, (len + 3)
738             // can possibly overflow, perform this operation as
739             // long and cast it back to integer when the value comes under
740             // integer limit. The final value will always be in integer
741             // limits
742             return 3 * (int) ((len + 3L) / 4) - paddings;
743         }
744 
745         /**
746          * Decodes base64 characters, and returns the number of data bytes
747          * written into the destination array.
748          *
749          * It is the fast path for full 4-byte to 3-byte decoding w/o errors.
750          *
751          * decodeBlock() can be overridden by an arch-specific intrinsic.
752          * decodeBlock can choose to decode all, none, or a variable-sized
753          * prefix of the src bytes.  This allows the intrinsic to decode in
754          * chunks of the src that are of a favorable size for the specific
755          * processor it's running on.
756          *
757          * If any illegal base64 bytes are encountered in src by the
758          * intrinsic, the intrinsic must return the actual number of valid
759          * data bytes already written to dst.  Note that the '=' pad
760          * character is treated as an illegal Base64 character by
761          * decodeBlock, so it will not process a block of 4 bytes
762          * containing pad characters.  However, MIME decoding ignores
763          * illegal characters, so any intrinsic overriding decodeBlock
764          * can choose how to handle illegal characters based on the isMIME
765          * parameter.
766          *
767          * Given the parameters, no length check is possible on dst, so dst
768          * is assumed to be large enough to store the decoded bytes.
769          *
770          * @param  src
771          *         the source byte array of Base64 encoded bytes
772          * @param  sp
773          *         the offset into src array to begin reading
774          * @param  sl
775          *         the offset (exclusive) past the last byte to be converted.
776          * @param  dst
777          *         the destination byte array of decoded data bytes
778          * @param  dp
779          *         the offset into dst array to begin writing
780          * @param  isURL
781          *         boolean, when true decode RFC4648 URL-safe base64 characters
782          * @param  isMIME
783          *         boolean, when true decode according to RFC2045 (ignore illegal chars)
784          * @return the number of destination data bytes produced
785          */
786         @IntrinsicCandidate
decodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL, boolean isMIME)787         private int decodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL, boolean isMIME) {
788             int[] base64 = isURL ? fromBase64URL : fromBase64;
789             int sl0 = sp + ((sl - sp) & ~0b11);
790             int new_dp = dp;
791             while (sp < sl0) {
792                 int b1 = base64[src[sp++] & 0xff];
793                 int b2 = base64[src[sp++] & 0xff];
794                 int b3 = base64[src[sp++] & 0xff];
795                 int b4 = base64[src[sp++] & 0xff];
796                 if ((b1 | b2 | b3 | b4) < 0) {    // non base64 byte
797                     return new_dp - dp;
798                 }
799                 int bits0 = b1 << 18 | b2 << 12 | b3 << 6 | b4;
800                 dst[new_dp++] = (byte)(bits0 >> 16);
801                 dst[new_dp++] = (byte)(bits0 >>  8);
802                 dst[new_dp++] = (byte)(bits0);
803             }
804             return new_dp - dp;
805         }
806 
decode0(byte[] src, int sp, int sl, byte[] dst)807         private int decode0(byte[] src, int sp, int sl, byte[] dst) {
808             int[] base64 = isURL ? fromBase64URL : fromBase64;
809             int dp = 0;
810             int bits = 0;
811             int shiftto = 18;       // pos of first byte of 4-byte atom
812 
813             while (sp < sl) {
814                 if (shiftto == 18 && sp < sl - 4) {       // fast path
815                     int dl = decodeBlock(src, sp, sl, dst, dp, isURL, isMIME);
816                     /*
817                      * Calculate how many characters were processed by how many
818                      * bytes of data were returned.
819                      */
820                     int chars_decoded = ((dl + 2) / 3) * 4;
821 
822                     sp += chars_decoded;
823                     dp += dl;
824                 }
825                 if (sp >= sl) {
826                     // we're done
827                     break;
828                 }
829                 int b = src[sp++] & 0xff;
830                 if ((b = base64[b]) < 0) {
831                     if (b == -2) {         // padding byte '='
832                         // =     shiftto==18 unnecessary padding
833                         // x=    shiftto==12 a dangling single x
834                         // x     to be handled together with non-padding case
835                         // xx=   shiftto==6&&sp==sl missing last =
836                         // xx=y  shiftto==6 last is not =
837                         if (shiftto == 6 && (sp == sl || src[sp++] != '=') ||
838                             shiftto == 18) {
839                             throw new IllegalArgumentException(
840                                 "Input byte array has wrong 4-byte ending unit");
841                         }
842                         break;
843                     }
844                     if (isMIME)    // skip if for rfc2045
845                         continue;
846                     else
847                         throw new IllegalArgumentException(
848                             "Illegal base64 character " +
849                             Integer.toString(src[sp - 1], 16));
850                 }
851                 bits |= (b << shiftto);
852                 shiftto -= 6;
853                 if (shiftto < 0) {
854                     dst[dp++] = (byte)(bits >> 16);
855                     dst[dp++] = (byte)(bits >>  8);
856                     dst[dp++] = (byte)(bits);
857                     shiftto = 18;
858                     bits = 0;
859                 }
860             }
861             // reached end of byte array or hit padding '=' characters.
862             if (shiftto == 6) {
863                 dst[dp++] = (byte)(bits >> 16);
864             } else if (shiftto == 0) {
865                 dst[dp++] = (byte)(bits >> 16);
866                 dst[dp++] = (byte)(bits >>  8);
867             } else if (shiftto == 12) {
868                 // dangling single "x", incorrectly encoded.
869                 throw new IllegalArgumentException(
870                     "Last unit does not have enough valid bits");
871             }
872             // anything left is invalid, if is not MIME.
873             // if MIME, ignore all non-base64 character
874             while (sp < sl) {
875                 if (isMIME && base64[src[sp++] & 0xff] < 0)
876                     continue;
877                 throw new IllegalArgumentException(
878                     "Input byte array has incorrect ending byte at " + sp);
879             }
880             return dp;
881         }
882     }
883 
884     /*
885      * An output stream for encoding bytes into the Base64.
886      */
887     private static class EncOutputStream extends FilterOutputStream {
888 
889         private int leftover = 0;
890         private int b0, b1, b2;
891         private boolean closed = false;
892 
893         private final char[] base64;    // byte->base64 mapping
894         private final byte[] newline;   // line separator, if needed
895         private final int linemax;
896         private final boolean doPadding;// whether or not to pad
897         private int linepos = 0;
898         private byte[] buf;
899 
EncOutputStream(OutputStream os, char[] base64, byte[] newline, int linemax, boolean doPadding)900         EncOutputStream(OutputStream os, char[] base64,
901                         byte[] newline, int linemax, boolean doPadding) {
902             super(os);
903             this.base64 = base64;
904             this.newline = newline;
905             this.linemax = linemax;
906             this.doPadding = doPadding;
907             this.buf = new byte[linemax <= 0 ? 8124 : linemax];
908         }
909 
910         @Override
write(int b)911         public void write(int b) throws IOException {
912             byte[] buf = new byte[1];
913             buf[0] = (byte)(b & 0xff);
914             write(buf, 0, 1);
915         }
916 
checkNewline()917         private void checkNewline() throws IOException {
918             if (linepos == linemax) {
919                 out.write(newline);
920                 linepos = 0;
921             }
922         }
923 
writeb4(char b1, char b2, char b3, char b4)924         private void writeb4(char b1, char b2, char b3, char b4) throws IOException {
925             buf[0] = (byte)b1;
926             buf[1] = (byte)b2;
927             buf[2] = (byte)b3;
928             buf[3] = (byte)b4;
929             out.write(buf, 0, 4);
930         }
931 
932         @Override
write(byte[] b, int off, int len)933         public void write(byte[] b, int off, int len) throws IOException {
934             if (closed)
935                 throw new IOException("Stream is closed");
936             if (off < 0 || len < 0 || len > b.length - off)
937                 throw new ArrayIndexOutOfBoundsException();
938             if (len == 0)
939                 return;
940             if (leftover != 0) {
941                 if (leftover == 1) {
942                     b1 = b[off++] & 0xff;
943                     len--;
944                     if (len == 0) {
945                         leftover++;
946                         return;
947                     }
948                 }
949                 b2 = b[off++] & 0xff;
950                 len--;
951                 checkNewline();
952                 writeb4(base64[b0 >> 2],
953                         base64[(b0 << 4) & 0x3f | (b1 >> 4)],
954                         base64[(b1 << 2) & 0x3f | (b2 >> 6)],
955                         base64[b2 & 0x3f]);
956                 linepos += 4;
957             }
958             int nBits24 = len / 3;
959             leftover = len - (nBits24 * 3);
960 
961             while (nBits24 > 0) {
962                 checkNewline();
963                 int dl = linemax <= 0 ? buf.length : buf.length - linepos;
964                 int sl = off + Math.min(nBits24, dl / 4) * 3;
965                 int dp = 0;
966                 for (int sp = off; sp < sl; ) {
967                     int bits = (b[sp++] & 0xff) << 16 |
968                                (b[sp++] & 0xff) <<  8 |
969                                (b[sp++] & 0xff);
970                     buf[dp++] = (byte)base64[(bits >>> 18) & 0x3f];
971                     buf[dp++] = (byte)base64[(bits >>> 12) & 0x3f];
972                     buf[dp++] = (byte)base64[(bits >>> 6)  & 0x3f];
973                     buf[dp++] = (byte)base64[bits & 0x3f];
974                 }
975                 out.write(buf, 0, dp);
976                 off = sl;
977                 linepos += dp;
978                 nBits24 -= dp / 4;
979             }
980             if (leftover == 1) {
981                 b0 = b[off++] & 0xff;
982             } else if (leftover == 2) {
983                 b0 = b[off++] & 0xff;
984                 b1 = b[off++] & 0xff;
985             }
986         }
987 
988         @Override
close()989         public void close() throws IOException {
990             if (!closed) {
991                 closed = true;
992                 if (leftover == 1) {
993                     checkNewline();
994                     out.write(base64[b0 >> 2]);
995                     out.write(base64[(b0 << 4) & 0x3f]);
996                     if (doPadding) {
997                         out.write('=');
998                         out.write('=');
999                     }
1000                 } else if (leftover == 2) {
1001                     checkNewline();
1002                     out.write(base64[b0 >> 2]);
1003                     out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
1004                     out.write(base64[(b1 << 2) & 0x3f]);
1005                     if (doPadding) {
1006                        out.write('=');
1007                     }
1008                 }
1009                 leftover = 0;
1010                 out.close();
1011             }
1012         }
1013     }
1014 
1015     /*
1016      * An input stream for decoding Base64 bytes
1017      */
1018     private static class DecInputStream extends InputStream {
1019 
1020         private final InputStream is;
1021         private final boolean isMIME;
1022         private final int[] base64;     // base64 -> byte mapping
1023         private int bits = 0;           // 24-bit buffer for decoding
1024 
1025         /* writing bit pos inside bits; one of 24 (left, msb), 18, 12, 6, 0 */
1026         private int wpos = 0;
1027 
1028         /* reading bit pos inside bits: one of 24 (left, msb), 16, 8, 0 */
1029         private int rpos = 0;
1030 
1031         private boolean eof = false;
1032         private boolean closed = false;
1033 
DecInputStream(InputStream is, int[] base64, boolean isMIME)1034         DecInputStream(InputStream is, int[] base64, boolean isMIME) {
1035             this.is = is;
1036             this.base64 = base64;
1037             this.isMIME = isMIME;
1038         }
1039 
1040         private byte[] sbBuf = new byte[1];
1041 
1042         @Override
read()1043         public int read() throws IOException {
1044             return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff;
1045         }
1046 
leftovers(byte[] b, int off, int pos, int limit)1047         private int leftovers(byte[] b, int off, int pos, int limit) {
1048             eof = true;
1049 
1050             /*
1051              * We use a loop here, as this method is executed only a few times.
1052              * Unrolling the loop would probably not contribute much here.
1053              */
1054             while (rpos - 8 >= wpos && pos != limit) {
1055                 rpos -= 8;
1056                 b[pos++] = (byte) (bits >> rpos);
1057             }
1058             return pos - off != 0 || rpos - 8 >= wpos ? pos - off : -1;
1059         }
1060 
eof(byte[] b, int off, int pos, int limit)1061         private int eof(byte[] b, int off, int pos, int limit) throws IOException {
1062             /*
1063              * pos != limit
1064              *
1065              * wpos == 18: x     dangling single x, invalid unit
1066              * accept ending xx or xxx without padding characters
1067              */
1068             if (wpos == 18) {
1069                 throw new IOException("Base64 stream has one un-decoded dangling byte.");
1070             }
1071             rpos = 24;
1072             return leftovers(b, off, pos, limit);
1073         }
1074 
padding(byte[] b, int off, int pos, int limit)1075         private int padding(byte[] b, int off, int pos, int limit) throws IOException {
1076             /*
1077              * pos != limit
1078              *
1079              * wpos == 24: =    (unnecessary padding)
1080              * wpos == 18: x=   (dangling single x, invalid unit)
1081              * wpos == 12 and missing last '=': xx=  (invalid padding)
1082              * wpos == 12 and last is not '=': xx=x (invalid padding)
1083              */
1084             if (wpos >= 18 || wpos == 12 && is.read() != '=') {
1085                 throw new IOException("Illegal base64 ending sequence:" + wpos);
1086             }
1087             rpos = 24;
1088             return leftovers(b, off, pos, limit);
1089         }
1090 
1091         @Override
read(byte[] b, int off, int len)1092         public int read(byte[] b, int off, int len) throws IOException {
1093             if (closed) {
1094                 throw new IOException("Stream is closed");
1095             }
1096             Objects.checkFromIndexSize(off, len, b.length);
1097             if (len == 0) {
1098                 return 0;
1099             }
1100 
1101             /*
1102              * Rather than keeping 2 running vars (e.g., off and len),
1103              * we only keep one (pos), while definitely fixing the boundaries
1104              * of the range [off, limit).
1105              * More specifically, each use of pos as an index in b meets
1106              *      pos - off >= 0 & limit - pos > 0
1107              *
1108              * Note that limit can overflow to Integer.MIN_VALUE. However,
1109              * as long as comparisons with pos are as coded, there's no harm.
1110              */
1111             int pos = off;
1112             final int limit = off + len;
1113             if (eof) {
1114                 return leftovers(b, off, pos, limit);
1115             }
1116 
1117             /*
1118              * Leftovers from previous invocation; here, wpos = 0.
1119              * There can be at most 2 leftover bytes (rpos <= 16).
1120              * Further, b has at least one free place.
1121              *
1122              * The logic could be coded as a loop, (as in method leftovers())
1123              * but the explicit "unrolling" makes it possible to generate
1124              * better byte extraction code.
1125              */
1126             if (rpos == 16) {
1127                 b[pos++] = (byte) (bits >> 8);
1128                 rpos = 8;
1129                 if (pos == limit) {
1130                     return len;
1131                 }
1132             }
1133             if (rpos == 8) {
1134                 b[pos++] = (byte) bits;
1135                 rpos = 0;
1136                 if (pos == limit) {
1137                     return len;
1138                 }
1139             }
1140 
1141             bits = 0;
1142             wpos = 24;
1143             for (;;) {
1144                 /* pos != limit & rpos == 0 */
1145                 final int i = is.read();
1146                 if (i < 0) {
1147                     return eof(b, off, pos, limit);
1148                 }
1149                 final int v = base64[i];
1150                 if (v < 0) {
1151                     /*
1152                      * i not in alphabet, thus
1153                      *      v == -2: i is '=', the padding
1154                      *      v == -1: i is something else, typically CR or LF
1155                      */
1156                     if (v == -1) {
1157                         if (isMIME) {
1158                             continue;
1159                         }
1160                         throw new IOException("Illegal base64 character 0x" +
1161                                 Integer.toHexString(i));
1162                     }
1163                     return padding(b, off, pos, limit);
1164                 }
1165                 wpos -= 6;
1166                 bits |= v << wpos;
1167                 if (wpos != 0) {
1168                     continue;
1169                 }
1170                 if (limit - pos >= 3) {
1171                     /* frequently taken fast path, no need to track rpos */
1172                     b[pos++] = (byte) (bits >> 16);
1173                     b[pos++] = (byte) (bits >> 8);
1174                     b[pos++] = (byte) bits;
1175                     bits = 0;
1176                     wpos = 24;
1177                     if (pos == limit) {
1178                         return len;
1179                     }
1180                     continue;
1181                 }
1182 
1183                 /* b has either 1 or 2 free places */
1184                 b[pos++] = (byte) (bits >> 16);
1185                 if (pos == limit) {
1186                     rpos = 16;
1187                     return len;
1188                 }
1189                 b[pos++] = (byte) (bits >> 8);
1190                 /* pos == limit, no need for an if */
1191                 rpos = 8;
1192                 return len;
1193             }
1194         }
1195 
1196         @Override
available()1197         public int available() throws IOException {
1198             if (closed)
1199                 throw new IOException("Stream is closed");
1200             return is.available();   // TBD:
1201         }
1202 
1203         @Override
close()1204         public void close() throws IOException {
1205             if (!closed) {
1206                 closed = true;
1207                 is.close();
1208             }
1209         }
1210     }
1211 }
1212