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