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 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      *
 190      * @see     Decoder
 191      * @since   1.8
 192      */
 193     public static class Encoder {
 194 
 195         private final byte[] newline;
 196         private final int linemax;
 197         private final boolean isURL;
 198         private final boolean doPadding;
 199 
 200         private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) {
 201             this.isURL = isURL;
 202             this.newline = newline;
 203             this.linemax = linemax;
 204             this.doPadding = doPadding;
 205         }
 206 
 207         /**
 208          * This array is a lookup table that translates 6-bit positive integer
 209          * index values into their "Base64 Alphabet" equivalents as specified
 210          * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
 211          */
 212         private static final char[] toBase64 = {
 213             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
 214             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
 215             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
 216             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
 217             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
 218         };
 219 
 220         /**
 221          * It's the lookup table for "URL and Filename safe Base64" as specified
 222          * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
 223          * '_'. This table is used when BASE64_URL is specified.
 224          */
 225         private static final char[] toBase64URL = {
 226             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
 227             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
 228             'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
 229             'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
 230             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
 231         };
 232 
 233         private static final int MIMELINEMAX = 76;
 234         private static final byte[] CRLF = new byte[] {'\r', '\n'};
 235 
 236         static final Encoder RFC4648 = new Encoder(false, null, -1, true);
 237         static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
 238         static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
 239 
 240         private final int outLength(int srclen) {
 241             int len = 0;
 242             if (doPadding) {
 243                 len = 4 * ((srclen + 2) / 3);
 244             } else {
 245                 int n = srclen % 3;
 246                 len = 4 * (srclen / 3) + (n == 0 ? 0 : n + 1);
 247             }
 248             if (linemax > 0)                                  // line separators
 249                 len += (len - 1) / linemax * newline.length;
 250             return len;
 251         }
 252 
 253         /**
 254          * Encodes all bytes from the specified byte array into a newly-allocated
 255          * byte array using the {@link Base64} encoding scheme. The returned byte
 256          * array is of the length of the resulting bytes.
 257          *
 258          * @param   src
 259          *          the byte array to encode
 260          * @return  A newly-allocated byte array containing the resulting
 261          *          encoded bytes.
 262          */
 263         public byte[] encode(byte[] src) {
 264             int len = outLength(src.length);          // dst array size
 265             byte[] dst = new byte[len];
 266             int ret = encode0(src, 0, src.length, dst);
 267             if (ret != dst.length)
 268                  return Arrays.copyOf(dst, ret);
 269             return dst;
 270         }
 271 
 272         /**
 273          * Encodes all bytes from the specified byte array using the
 274          * {@link Base64} encoding scheme, writing the resulting bytes to the
 275          * given output byte array, starting at offset 0.
 276          *
 277          * <p> It is the responsibility of the invoker of this method to make
 278          * sure the output byte array {@code dst} has enough space for encoding
 279          * all bytes from the input byte array. No bytes will be written to the
 280          * output byte array if the output byte array is not big enough.
 281          *
 282          * @param   src
 283          *          the byte array to encode
 284          * @param   dst
 285          *          the output byte array
 286          * @return  The number of bytes written to the output byte array
 287          *
 288          * @throws  IllegalArgumentException if {@code dst} does not have enough
 289          *          space for encoding all input bytes.
 290          */
 291         public int encode(byte[] src, byte[] dst) {
 292             int len = outLength(src.length);         // dst array size
 293             if (dst.length < len)
 294                 throw new IllegalArgumentException(
 295                     "Output byte array is too small for encoding all input bytes");
 296             return encode0(src, 0, src.length, dst);
 297         }
 298 
 299         /**
 300          * Encodes the specified byte array into a String using the {@link Base64}
 301          * encoding scheme.
 302          *
 303          * <p> This method first encodes all input bytes into a base64 encoded
 304          * byte array and then constructs a new String by using the encoded byte
 305          * array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1
 306          * ISO-8859-1} charset.
 307          *
 308          * <p> In other words, an invocation of this method has exactly the same
 309          * effect as invoking
 310          * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.
 311          *
 312          * @param   src
 313          *          the byte array to encode
 314          * @return  A String containing the resulting Base64 encoded characters
 315          */
 316         @SuppressWarnings("deprecation")
 317         public String encodeToString(byte[] src) {
 318             byte[] encoded = encode(src);
 319             return new String(encoded, 0, 0, encoded.length);
 320         }
 321 
 322         /**
 323          * Encodes all remaining bytes from the specified byte buffer into
 324          * a newly-allocated ByteBuffer using the {@link Base64} encoding
 325          * scheme.
 326          *
 327          * Upon return, the source buffer's position will be updated to
 328          * its limit; its limit will not have been changed. The returned
 329          * output buffer's position will be zero and its limit will be the
 330          * number of resulting encoded bytes.
 331          *
 332          * @param   buffer
 333          *          the source ByteBuffer to encode
 334          * @return  A newly-allocated byte buffer containing the encoded bytes.
 335          */
 336         public ByteBuffer encode(ByteBuffer buffer) {
 337             int len = outLength(buffer.remaining());
 338             byte[] dst = new byte[len];
 339             int ret = 0;
 340             if (buffer.hasArray()) {
 341                 ret = encode0(buffer.array(),
 342                               buffer.arrayOffset() + buffer.position(),
 343                               buffer.arrayOffset() + buffer.limit(),
 344                               dst);
 345                 buffer.position(buffer.limit());
 346             } else {
 347                 byte[] src = new byte[buffer.remaining()];
 348                 buffer.get(src);
 349                 ret = encode0(src, 0, src.length, dst);
 350             }
 351             if (ret != dst.length)
 352                  dst = Arrays.copyOf(dst, ret);
 353             return ByteBuffer.wrap(dst);
 354         }
 355 
 356         /**
 357          * Wraps an output stream for encoding byte data using the {@link Base64}
 358          * encoding scheme.
 359          *
 360          * <p> It is recommended to promptly close the returned output stream after
 361          * use, during which it will flush all possible leftover bytes to the underlying
 362          * output stream. Closing the returned output stream will close the underlying
 363          * output stream.
 364          *
 365          * @param   os
 366          *          the output stream.
 367          * @return  the output stream for encoding the byte data into the
 368          *          specified Base64 encoded format
 369          */
 370         public OutputStream wrap(OutputStream os) {
 371             Objects.requireNonNull(os);
 372             return new EncOutputStream(os, isURL ? toBase64URL : toBase64,
 373                                        newline, linemax, doPadding);
 374         }
 375 
 376         /**
 377          * Returns an encoder instance that encodes equivalently to this one,
 378          * but without adding any padding character at the end of the encoded
 379          * byte data.
 380          *
 381          * <p> The encoding scheme of this encoder instance is unaffected by
 382          * this invocation. The returned encoder instance should be used for
 383          * non-padding encoding operation.
 384          *
 385          * @return an equivalent encoder that encodes without adding any
 386          *         padding character at the end
 387          */
 388         public Encoder withoutPadding() {
 389             if (!doPadding)
 390                 return this;
 391             return new Encoder(isURL, newline, linemax, false);
 392         }
 393 
 394         @HotSpotIntrinsicCandidate
 395         private void encodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL) {
 396             char[] base64 = isURL ? toBase64URL : toBase64;
 397             for (int sp0 = sp, dp0 = dp ; sp0 < sl; ) {
 398                 int bits = (src[sp0++] & 0xff) << 16 |
 399                            (src[sp0++] & 0xff) <<  8 |
 400                            (src[sp0++] & 0xff);
 401                 dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
 402                 dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
 403                 dst[dp0++] = (byte)base64[(bits >>> 6)  & 0x3f];
 404                 dst[dp0++] = (byte)base64[bits & 0x3f];
 405             }
 406         }
 407 
 408         private int encode0(byte[] src, int off, int end, byte[] dst) {
 409             char[] base64 = isURL ? toBase64URL : toBase64;
 410             int sp = off;
 411             int slen = (end - off) / 3 * 3;
 412             int sl = off + slen;
 413             if (linemax > 0 && slen  > linemax / 4 * 3)
 414                 slen = linemax / 4 * 3;
 415             int dp = 0;
 416             while (sp < sl) {
 417                 int sl0 = Math.min(sp + slen, sl);
 418                 encodeBlock(src, sp, sl0, dst, dp, isURL);
 419                 int dlen = (sl0 - sp) / 3 * 4;
 420                 dp += dlen;
 421                 sp = sl0;
 422                 if (dlen == linemax && sp < end) {
 423                     for (byte b : newline){
 424                         dst[dp++] = b;
 425                     }
 426                 }
 427             }
 428             if (sp < end) {               // 1 or 2 leftover bytes
 429                 int b0 = src[sp++] & 0xff;
 430                 dst[dp++] = (byte)base64[b0 >> 2];
 431                 if (sp == end) {
 432                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
 433                     if (doPadding) {
 434                         dst[dp++] = '=';
 435                         dst[dp++] = '=';
 436                     }
 437                 } else {
 438                     int b1 = src[sp++] & 0xff;
 439                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
 440                     dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
 441                     if (doPadding) {
 442                         dst[dp++] = '=';
 443                     }
 444                 }
 445             }
 446             return dp;
 447         }
 448     }
 449 
 450     /**
 451      * This class implements a decoder for decoding byte data using the
 452      * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
 453      *
 454      * <p> The Base64 padding character {@code '='} is accepted and
 455      * interpreted as the end of the encoded byte data, but is not
 456      * required. So if the final unit of the encoded byte data only has
 457      * two or three Base64 characters (without the corresponding padding
 458      * character(s) padded), they are decoded as if followed by padding
 459      * character(s). If there is a padding character present in the
 460      * final unit, the correct number of padding character(s) must be
 461      * present, otherwise {@code IllegalArgumentException} (
 462      * {@code IOException} when reading from a Base64 stream) is thrown
 463      * during decoding.
 464      *
 465      * <p> Instances of {@link Decoder} class are safe for use by
 466      * multiple concurrent threads.
 467      *
 468      * <p> Unless otherwise noted, passing a {@code null} argument to
 469      * a method of this class will cause a
 470      * {@link java.lang.NullPointerException NullPointerException} to
 471      * be thrown.
 472      *
 473      * @see     Encoder
 474      * @since   1.8
 475      */
 476     public static class Decoder {
 477 
 478         private final boolean isURL;
 479         private final boolean isMIME;
 480 
 481         private Decoder(boolean isURL, boolean isMIME) {
 482             this.isURL = isURL;
 483             this.isMIME = isMIME;
 484         }
 485 
 486         /**
 487          * Lookup table for decoding unicode characters drawn from the
 488          * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into
 489          * their 6-bit positive integer equivalents.  Characters that
 490          * are not in the Base64 alphabet but fall within the bounds of
 491          * the array are encoded to -1.
 492          *
 493          */
 494         private static final int[] fromBase64 = new int[256];
 495         static {
 496             Arrays.fill(fromBase64, -1);
 497             for (int i = 0; i < Encoder.toBase64.length; i++)
 498                 fromBase64[Encoder.toBase64[i]] = i;
 499             fromBase64['='] = -2;
 500         }
 501 
 502         /**
 503          * Lookup table for decoding "URL and Filename safe Base64 Alphabet"
 504          * as specified in Table2 of the RFC 4648.
 505          */
 506         private static final int[] fromBase64URL = new int[256];
 507 
 508         static {
 509             Arrays.fill(fromBase64URL, -1);
 510             for (int i = 0; i < Encoder.toBase64URL.length; i++)
 511                 fromBase64URL[Encoder.toBase64URL[i]] = i;
 512             fromBase64URL['='] = -2;
 513         }
 514 
 515         static final Decoder RFC4648         = new Decoder(false, false);
 516         static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
 517         static final Decoder RFC2045         = new Decoder(false, true);
 518 
 519         /**
 520          * Decodes all bytes from the input byte array using the {@link Base64}
 521          * encoding scheme, writing the results into a newly-allocated output
 522          * byte array. The returned byte array is of the length of the resulting
 523          * bytes.
 524          *
 525          * @param   src
 526          *          the byte array to decode
 527          *
 528          * @return  A newly-allocated byte array containing the decoded bytes.
 529          *
 530          * @throws  IllegalArgumentException
 531          *          if {@code src} is not in valid Base64 scheme
 532          */
 533         public byte[] decode(byte[] src) {
 534             byte[] dst = new byte[outLength(src, 0, src.length)];
 535             int ret = decode0(src, 0, src.length, dst);
 536             if (ret != dst.length) {
 537                 dst = Arrays.copyOf(dst, ret);
 538             }
 539             return dst;
 540         }
 541 
 542         /**
 543          * Decodes a Base64 encoded String into a newly-allocated byte array
 544          * using the {@link Base64} encoding scheme.
 545          *
 546          * <p> An invocation of this method has exactly the same effect as invoking
 547          * {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))}
 548          *
 549          * @param   src
 550          *          the string to decode
 551          *
 552          * @return  A newly-allocated byte array containing the decoded bytes.
 553          *
 554          * @throws  IllegalArgumentException
 555          *          if {@code src} is not in valid Base64 scheme
 556          */
 557         public byte[] decode(String src) {
 558             return decode(src.getBytes(StandardCharsets.ISO_8859_1));
 559         }
 560 
 561         /**
 562          * Decodes all bytes from the input byte array using the {@link Base64}
 563          * encoding scheme, writing the results into the given output byte array,
 564          * starting at offset 0.
 565          *
 566          * <p> It is the responsibility of the invoker of this method to make
 567          * sure the output byte array {@code dst} has enough space for decoding
 568          * all bytes from the input byte array. No bytes will be written to
 569          * the output byte array if the output byte array is not big enough.
 570          *
 571          * <p> If the input byte array is not in valid Base64 encoding scheme
 572          * then some bytes may have been written to the output byte array before
 573          * IllegalargumentException is thrown.
 574          *
 575          * @param   src
 576          *          the byte array to decode
 577          * @param   dst
 578          *          the output byte array
 579          *
 580          * @return  The number of bytes written to the output byte array
 581          *
 582          * @throws  IllegalArgumentException
 583          *          if {@code src} is not in valid Base64 scheme, or {@code dst}
 584          *          does not have enough space for decoding all input bytes.
 585          */
 586         public int decode(byte[] src, byte[] dst) {
 587             int len = outLength(src, 0, src.length);
 588             if (dst.length < len)
 589                 throw new IllegalArgumentException(
 590                     "Output byte array is too small for decoding all input bytes");
 591             return decode0(src, 0, src.length, dst);
 592         }
 593 
 594         /**
 595          * Decodes all bytes from the input byte buffer using the {@link Base64}
 596          * encoding scheme, writing the results into a newly-allocated ByteBuffer.
 597          *
 598          * <p> Upon return, the source buffer's position will be updated to
 599          * its limit; its limit will not have been changed. The returned
 600          * output buffer's position will be zero and its limit will be the
 601          * number of resulting decoded bytes
 602          *
 603          * <p> {@code IllegalArgumentException} is thrown if the input buffer
 604          * is not in valid Base64 encoding scheme. The position of the input
 605          * buffer will not be advanced in this case.
 606          *
 607          * @param   buffer
 608          *          the ByteBuffer to decode
 609          *
 610          * @return  A newly-allocated byte buffer containing the decoded bytes
 611          *
 612          * @throws  IllegalArgumentException
 613          *          if {@code src} is not in valid Base64 scheme.
 614          */
 615         public ByteBuffer decode(ByteBuffer buffer) {
 616             int pos0 = buffer.position();
 617             try {
 618                 byte[] src;
 619                 int sp, sl;
 620                 if (buffer.hasArray()) {
 621                     src = buffer.array();
 622                     sp = buffer.arrayOffset() + buffer.position();
 623                     sl = buffer.arrayOffset() + buffer.limit();
 624                     buffer.position(buffer.limit());
 625                 } else {
 626                     src = new byte[buffer.remaining()];
 627                     buffer.get(src);
 628                     sp = 0;
 629                     sl = src.length;
 630                 }
 631                 byte[] dst = new byte[outLength(src, sp, sl)];
 632                 return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst));
 633             } catch (IllegalArgumentException iae) {
 634                 buffer.position(pos0);
 635                 throw iae;
 636             }
 637         }
 638 
 639         /**
 640          * Returns an input stream for decoding {@link Base64} encoded byte stream.
 641          *
 642          * <p> The {@code read}  methods of the returned {@code InputStream} will
 643          * throw {@code IOException} when reading bytes that cannot be decoded.
 644          *
 645          * <p> Closing the returned input stream will close the underlying
 646          * input stream.
 647          *
 648          * @param   is
 649          *          the input stream
 650          *
 651          * @return  the input stream for decoding the specified Base64 encoded
 652          *          byte stream
 653          */
 654         public InputStream wrap(InputStream is) {
 655             Objects.requireNonNull(is);
 656             return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
 657         }
 658 
 659         private int outLength(byte[] src, int sp, int sl) {
 660             int[] base64 = isURL ? fromBase64URL : fromBase64;
 661             int paddings = 0;
 662             int len = sl - sp;
 663             if (len == 0)
 664                 return 0;
 665             if (len < 2) {
 666                 if (isMIME && base64[0] == -1)
 667                     return 0;
 668                 throw new IllegalArgumentException(
 669                     "Input byte[] should at least have 2 bytes for base64 bytes");
 670             }
 671             if (isMIME) {
 672                 // scan all bytes to fill out all non-alphabet. a performance
 673                 // trade-off of pre-scan or Arrays.copyOf
 674                 int n = 0;
 675                 while (sp < sl) {
 676                     int b = src[sp++] & 0xff;
 677                     if (b == '=') {
 678                         len -= (sl - sp + 1);
 679                         break;
 680                     }
 681                     if ((b = base64[b]) == -1)
 682                         n++;
 683                 }
 684                 len -= n;
 685             } else {
 686                 if (src[sl - 1] == '=') {
 687                     paddings++;
 688                     if (src[sl - 2] == '=')
 689                         paddings++;
 690                 }
 691             }
 692             if (paddings == 0 && (len & 0x3) !=  0)
 693                 paddings = 4 - (len & 0x3);
 694             return 3 * ((len + 3) / 4) - paddings;
 695         }
 696 
 697         private int decode0(byte[] src, int sp, int sl, byte[] dst) {
 698             int[] base64 = isURL ? fromBase64URL : fromBase64;
 699             int dp = 0;
 700             int bits = 0;
 701             int shiftto = 18;       // pos of first byte of 4-byte atom
 702 
 703             while (sp < sl) {
 704                 if (shiftto == 18 && sp + 4 < sl) {       // fast path
 705                     int sl0 = sp + ((sl - sp) & ~0b11);
 706                     while (sp < sl0) {
 707                         int b1 = base64[src[sp++] & 0xff];
 708                         int b2 = base64[src[sp++] & 0xff];
 709                         int b3 = base64[src[sp++] & 0xff];
 710                         int b4 = base64[src[sp++] & 0xff];
 711                         if ((b1 | b2 | b3 | b4) < 0) {    // non base64 byte
 712                             sp -= 4;
 713                             break;
 714                         }
 715                         int bits0 = b1 << 18 | b2 << 12 | b3 << 6 | b4;
 716                         dst[dp++] = (byte)(bits0 >> 16);
 717                         dst[dp++] = (byte)(bits0 >>  8);
 718                         dst[dp++] = (byte)(bits0);
 719                     }
 720                     if (sp >= sl)
 721                         break;
 722                 }
 723                 int b = src[sp++] & 0xff;
 724                 if ((b = base64[b]) < 0) {
 725                     if (b == -2) {         // padding byte '='
 726                         // =     shiftto==18 unnecessary padding
 727                         // x=    shiftto==12 a dangling single x
 728                         // x     to be handled together with non-padding case
 729                         // xx=   shiftto==6&&sp==sl missing last =
 730                         // xx=y  shiftto==6 last is not =
 731                         if (shiftto == 6 && (sp == sl || src[sp++] != '=') ||
 732                             shiftto == 18) {
 733                             throw new IllegalArgumentException(
 734                                 "Input byte array has wrong 4-byte ending unit");
 735                         }
 736                         break;
 737                     }
 738                     if (isMIME)    // skip if for rfc2045
 739                         continue;
 740                     else
 741                         throw new IllegalArgumentException(
 742                             "Illegal base64 character " +
 743                             Integer.toString(src[sp - 1], 16));
 744                 }
 745                 bits |= (b << shiftto);
 746                 shiftto -= 6;
 747                 if (shiftto < 0) {
 748                     dst[dp++] = (byte)(bits >> 16);
 749                     dst[dp++] = (byte)(bits >>  8);
 750                     dst[dp++] = (byte)(bits);
 751                     shiftto = 18;
 752                     bits = 0;
 753                 }
 754             }
 755             // reached end of byte array or hit padding '=' characters.
 756             if (shiftto == 6) {
 757                 dst[dp++] = (byte)(bits >> 16);
 758             } else if (shiftto == 0) {
 759                 dst[dp++] = (byte)(bits >> 16);
 760                 dst[dp++] = (byte)(bits >>  8);
 761             } else if (shiftto == 12) {
 762                 // dangling single "x", incorrectly encoded.
 763                 throw new IllegalArgumentException(
 764                     "Last unit does not have enough valid bits");
 765             }
 766             // anything left is invalid, if is not MIME.
 767             // if MIME, ignore all non-base64 character
 768             while (sp < sl) {
 769                 if (isMIME && base64[src[sp++] & 0xff] < 0)
 770                     continue;
 771                 throw new IllegalArgumentException(
 772                     "Input byte array has incorrect ending byte at " + sp);
 773             }
 774             return dp;
 775         }
 776     }
 777 
 778     /*
 779      * An output stream for encoding bytes into the Base64.
 780      */
 781     private static class EncOutputStream extends FilterOutputStream {
 782 
 783         private int leftover = 0;
 784         private int b0, b1, b2;
 785         private boolean closed = false;
 786 
 787         private final char[] base64;    // byte->base64 mapping
 788         private final byte[] newline;   // line separator, if needed
 789         private final int linemax;
 790         private final boolean doPadding;// whether or not to pad
 791         private int linepos = 0;
 792         private byte[] buf;
 793 
 794         EncOutputStream(OutputStream os, char[] base64,
 795                         byte[] newline, int linemax, boolean doPadding) {
 796             super(os);
 797             this.base64 = base64;
 798             this.newline = newline;
 799             this.linemax = linemax;
 800             this.doPadding = doPadding;
 801             this.buf = new byte[linemax <= 0 ? 8124 : linemax];
 802         }
 803 
 804         @Override
 805         public void write(int b) throws IOException {
 806             byte[] buf = new byte[1];
 807             buf[0] = (byte)(b & 0xff);
 808             write(buf, 0, 1);
 809         }
 810 
 811         private void checkNewline() throws IOException {
 812             if (linepos == linemax) {
 813                 out.write(newline);
 814                 linepos = 0;
 815             }
 816         }
 817 
 818         private void writeb4(char b1, char b2, char b3, char b4) throws IOException {
 819             buf[0] = (byte)b1;
 820             buf[1] = (byte)b2;
 821             buf[2] = (byte)b3;
 822             buf[3] = (byte)b4;
 823             out.write(buf, 0, 4);
 824         }
 825 
 826         @Override
 827         public void write(byte[] b, int off, int len) throws IOException {
 828             if (closed)
 829                 throw new IOException("Stream is closed");
 830             if (off < 0 || len < 0 || len > b.length - off)
 831                 throw new ArrayIndexOutOfBoundsException();
 832             if (len == 0)
 833                 return;
 834             if (leftover != 0) {
 835                 if (leftover == 1) {
 836                     b1 = b[off++] & 0xff;
 837                     len--;
 838                     if (len == 0) {
 839                         leftover++;
 840                         return;
 841                     }
 842                 }
 843                 b2 = b[off++] & 0xff;
 844                 len--;
 845                 checkNewline();
 846                 writeb4(base64[b0 >> 2],
 847                         base64[(b0 << 4) & 0x3f | (b1 >> 4)],
 848                         base64[(b1 << 2) & 0x3f | (b2 >> 6)],
 849                         base64[b2 & 0x3f]);
 850                 linepos += 4;
 851             }
 852             int nBits24 = len / 3;
 853             leftover = len - (nBits24 * 3);
 854 
 855             while (nBits24 > 0) {
 856                 checkNewline();
 857                 int dl = linemax <= 0 ? buf.length : buf.length - linepos;
 858                 int sl = off + Math.min(nBits24, dl / 4) * 3;
 859                 int dp = 0;
 860                 for (int sp = off; sp < sl; ) {
 861                     int bits = (b[sp++] & 0xff) << 16 |
 862                                (b[sp++] & 0xff) <<  8 |
 863                                (b[sp++] & 0xff);
 864                     buf[dp++] = (byte)base64[(bits >>> 18) & 0x3f];
 865                     buf[dp++] = (byte)base64[(bits >>> 12) & 0x3f];
 866                     buf[dp++] = (byte)base64[(bits >>> 6)  & 0x3f];
 867                     buf[dp++] = (byte)base64[bits & 0x3f];
 868                 }
 869                 out.write(buf, 0, dp);
 870                 off = sl;
 871                 linepos += dp;
 872                 nBits24 -= dp / 4;
 873             }
 874             if (leftover == 1) {
 875                 b0 = b[off++] & 0xff;
 876             } else if (leftover == 2) {
 877                 b0 = b[off++] & 0xff;
 878                 b1 = b[off++] & 0xff;
 879             }
 880         }
 881 
 882         @Override
 883         public void close() throws IOException {
 884             if (!closed) {
 885                 closed = true;
 886                 if (leftover == 1) {
 887                     checkNewline();
 888                     out.write(base64[b0 >> 2]);
 889                     out.write(base64[(b0 << 4) & 0x3f]);
 890                     if (doPadding) {
 891                         out.write('=');
 892                         out.write('=');
 893                     }
 894                 } else if (leftover == 2) {
 895                     checkNewline();
 896                     out.write(base64[b0 >> 2]);
 897                     out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
 898                     out.write(base64[(b1 << 2) & 0x3f]);
 899                     if (doPadding) {
 900                        out.write('=');
 901                     }
 902                 }
 903                 leftover = 0;
 904                 out.close();
 905             }
 906         }
 907     }
 908 
 909     /*
 910      * An input stream for decoding Base64 bytes
 911      */
 912     private static class DecInputStream extends InputStream {
 913 
 914         private final InputStream is;
 915         private final boolean isMIME;
 916         private final int[] base64;      // base64 -> byte mapping
 917         private int bits = 0;            // 24-bit buffer for decoding
 918         private int nextin = 18;         // next available "off" in "bits" for input;
 919                                          // -> 18, 12, 6, 0
 920         private int nextout = -8;        // next available "off" in "bits" for output;
 921                                          // -> 8, 0, -8 (no byte for output)
 922         private boolean eof = false;
 923         private boolean closed = false;
 924 
 925         DecInputStream(InputStream is, int[] base64, boolean isMIME) {
 926             this.is = is;
 927             this.base64 = base64;
 928             this.isMIME = isMIME;
 929         }
 930 
 931         private byte[] sbBuf = new byte[1];
 932 
 933         @Override
 934         public int read() throws IOException {
 935             return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff;
 936         }
 937 
 938         private int eof(byte[] b, int off, int len, int oldOff)
 939             throws IOException
 940         {
 941             eof = true;
 942             if (nextin != 18) {
 943                 if (nextin == 12)
 944                     throw new IOException("Base64 stream has one un-decoded dangling byte.");
 945                 // treat ending xx/xxx without padding character legal.
 946                 // same logic as v == '=' below
 947                 b[off++] = (byte)(bits >> (16));
 948                 if (nextin == 0) {           // only one padding byte
 949                     if (len == 1) {          // no enough output space
 950                         bits >>= 8;          // shift to lowest byte
 951                         nextout = 0;
 952                     } else {
 953                         b[off++] = (byte) (bits >>  8);
 954                     }
 955                 }
 956             }
 957             return off == oldOff ? -1 : off - oldOff;
 958         }
 959 
 960         private int padding(byte[] b, int off, int len, int oldOff)
 961             throws IOException
 962         {
 963             // =     shiftto==18 unnecessary padding
 964             // x=    shiftto==12 dangling x, invalid unit
 965             // xx=   shiftto==6 && missing last '='
 966             // xx=y  or last is not '='
 967             if (nextin == 18 || nextin == 12 ||
 968                 nextin == 6 && is.read() != '=') {
 969                 throw new IOException("Illegal base64 ending sequence:" + nextin);
 970             }
 971             b[off++] = (byte)(bits >> (16));
 972             if (nextin == 0) {           // only one padding byte
 973                 if (len == 1) {          // no enough output space
 974                     bits >>= 8;          // shift to lowest byte
 975                     nextout = 0;
 976                 } else {
 977                     b[off++] = (byte) (bits >>  8);
 978                 }
 979             }
 980             eof = true;
 981             return off - oldOff;
 982         }
 983 
 984         @Override
 985         public int read(byte[] b, int off, int len) throws IOException {
 986             if (closed)
 987                 throw new IOException("Stream is closed");
 988             if (eof && nextout < 0)    // eof and no leftover
 989                 return -1;
 990             if (off < 0 || len < 0 || len > b.length - off)
 991                 throw new IndexOutOfBoundsException();
 992             int oldOff = off;
 993             while (nextout >= 0) {       // leftover output byte(s) in bits buf
 994                 if (len == 0)
 995                     return off - oldOff;
 996                 b[off++] = (byte)(bits >> nextout);
 997                 len--;
 998                 nextout -= 8;
 999             }
1000             bits = 0;
1001             while (len > 0) {
1002                 int v = is.read();
1003                 if (v == -1) {
1004                     return eof(b, off, len, oldOff);
1005                 }
1006                 if ((v = base64[v]) < 0) {
1007                     if (v == -2) {       // padding byte(s)
1008                         return padding(b, off, len, oldOff);
1009                     }
1010                     if (v == -1) {
1011                         if (!isMIME)
1012                             throw new IOException("Illegal base64 character " +
1013                                 Integer.toString(v, 16));
1014                         continue;        // skip if for rfc2045
1015                     }
1016                     // neve be here
1017                 }
1018                 bits |= (v << nextin);
1019                 if (nextin == 0) {
1020                     nextin = 18;         // clear for next in
1021                     b[off++] = (byte)(bits >> 16);
1022                     if (len == 1) {
1023                         nextout = 8;    // 2 bytes left in bits
1024                         break;
1025                     }
1026                     b[off++] = (byte)(bits >> 8);
1027                     if (len == 2) {
1028                         nextout = 0;    // 1 byte left in bits
1029                         break;
1030                     }
1031                     b[off++] = (byte)bits;
1032                     len -= 3;
1033                     bits = 0;
1034                 } else {
1035                     nextin -= 6;
1036                 }
1037             }
1038             return off - oldOff;
1039         }
1040 
1041         @Override
1042         public int available() throws IOException {
1043             if (closed)
1044                 throw new IOException("Stream is closed");
1045             return is.available();   // TBD:
1046         }
1047 
1048         @Override
1049         public void close() throws IOException {
1050             if (!closed) {
1051                 closed = true;
1052                 is.close();
1053             }
1054         }
1055     }
1056 }