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