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