1 /*
   2  * Copyright (c) 2012, 2013, 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 name="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 name="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 name="mime"><b>MIME</b></a>
  59  * <p> Uses the "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 {@code lineLength <= 0} the output will not be separated
 120      *          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          if (lineLength <= 0) {
 139              return Encoder.RFC4648;
 140          }
 141          return new Encoder(false, lineSeparator, lineLength >> 2 << 2, true);
 142     }
 143 
 144     /**
 145      * Returns a {@link Decoder} that decodes using the
 146      * <a href="#basic">Basic</a> type base64 encoding scheme.
 147      *
 148      * @return  A Base64 decoder.
 149      */
 150     public static Decoder getDecoder() {
 151          return Decoder.RFC4648;
 152     }
 153 
 154     /**
 155      * Returns a {@link Decoder} that decodes using the
 156      * <a href="#url">URL and Filename safe</a> type base64
 157      * encoding scheme.
 158      *
 159      * @return  A Base64 decoder.
 160      */
 161     public static Decoder getUrlDecoder() {
 162          return Decoder.RFC4648_URLSAFE;
 163     }
 164 
 165     /**
 166      * Returns a {@link Decoder} that decodes using the
 167      * <a href="#mime">MIME</a> type base64 decoding scheme.
 168      *
 169      * @return  A Base64 decoder.
 170      */
 171     public static Decoder getMimeDecoder() {
 172          return Decoder.RFC2045;
 173     }
 174 
 175     /**
 176      * This class implements an encoder for encoding byte data using
 177      * the Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
 178      *
 179      * <p> Instances of {@link Encoder} class are safe for use by
 180      * multiple concurrent threads.
 181      *
 182      * <p> Unless otherwise noted, passing a {@code null} argument to
 183      * a method of this class will cause a
 184      * {@link java.lang.NullPointerException NullPointerException} to
 185      * be thrown.
 186      *
 187      * @see     Decoder
 188      * @since   1.8
 189      */
 190     public static class Encoder {
 191 
 192         private final byte[] newline;
 193         private final int linemax;
 194         private final boolean isURL;
 195         private final boolean doPadding;
 196 
 197         private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) {
 198             this.isURL = isURL;
 199             this.newline = newline;
 200             this.linemax = linemax;
 201             this.doPadding = doPadding;
 202         }
 203 
 204         /**
 205          * This array is a lookup table that translates 6-bit positive integer
 206          * index values into their "Base64 Alphabet" equivalents as specified
 207          * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
 208          */
 209         private static final char[] toBase64 = {
 210             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
 211             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
 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             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
 215         };
 216 
 217         /**
 218          * It's the lookup table for "URL and Filename safe Base64" as specified
 219          * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
 220          * '_'. This table is used when BASE64_URL is specified.
 221          */
 222         private static final char[] toBase64URL = {
 223             'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
 224             'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
 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             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
 228         };
 229 
 230         private static final int MIMELINEMAX = 76;
 231         private static final byte[] CRLF = new byte[] {'\r', '\n'};
 232 
 233         static final Encoder RFC4648 = new Encoder(false, null, -1, true);
 234         static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
 235         static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
 236 
 237         private final int outLength(int srclen) {
 238             int len = 0;
 239             if (doPadding) {
 240                 len = 4 * ((srclen + 2) / 3);
 241             } else {
 242                 int n = srclen % 3;
 243                 len = 4 * (srclen / 3) + (n == 0 ? 0 : n + 1);
 244             }
 245             if (linemax > 0)                                  // line separators
 246                 len += (len - 1) / linemax * newline.length;
 247             return len;
 248         }
 249 
 250         /**
 251          * Encodes all bytes from the specified byte array into a newly-allocated
 252          * byte array using the {@link Base64} encoding scheme. The returned byte
 253          * array is of the length of the resulting bytes.
 254          *
 255          * @param   src
 256          *          the byte array to encode
 257          * @return  A newly-allocated byte array containing the resulting
 258          *          encoded bytes.
 259          */
 260         public byte[] encode(byte[] src) {
 261             int len = outLength(src.length);          // dst array size
 262             byte[] dst = new byte[len];
 263             int ret = encode0(src, 0, src.length, dst);
 264             if (ret != dst.length)
 265                  return Arrays.copyOf(dst, ret);
 266             return dst;
 267         }
 268 
 269         /**
 270          * Encodes all bytes from the specified byte array using the
 271          * {@link Base64} encoding scheme, writing the resulting bytes to the
 272          * given output byte array, starting at offset 0.
 273          *
 274          * <p> It is the responsibility of the invoker of this method to make
 275          * sure the output byte array {@code dst} has enough space for encoding
 276          * all bytes from the input byte array. No bytes will be written to the
 277          * output byte array if the output byte array is not big enough.
 278          *
 279          * @param   src
 280          *          the byte array to encode
 281          * @param   dst
 282          *          the output byte array
 283          * @return  The number of bytes written to the output byte array
 284          *
 285          * @throws  IllegalArgumentException if {@code dst} does not have enough
 286          *          space for encoding all input bytes.
 287          */
 288         public int encode(byte[] src, byte[] dst) {
 289             int len = outLength(src.length);         // dst array size
 290             if (dst.length < len)
 291                 throw new IllegalArgumentException(
 292                     "Output byte array is too small for encoding all input bytes");
 293             return encode0(src, 0, src.length, dst);
 294         }
 295 
 296         /**
 297          * Encodes the specified byte array into a String using the {@link Base64}
 298          * encoding scheme.
 299          *
 300          * <p> This method first encodes all input bytes into a base64 encoded
 301          * byte array and then constructs a new String by using the encoded byte
 302          * array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1
 303          * ISO-8859-1} charset.
 304          *
 305          * <p> In other words, an invocation of this method has exactly the same
 306          * effect as invoking
 307          * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.
 308          *
 309          * @param   src
 310          *          the byte array to encode
 311          * @return  A String containing the resulting Base64 encoded characters
 312          */
 313         @SuppressWarnings("deprecation")
 314         public String encodeToString(byte[] src) {
 315             byte[] encoded = encode(src);
 316             return new String(encoded, 0, 0, encoded.length);
 317         }
 318 
 319         /**
 320          * Encodes all remaining bytes from the specified byte buffer into
 321          * a newly-allocated ByteBuffer using the {@link Base64} encoding
 322          * scheme.
 323          *
 324          * Upon return, the source buffer's position will be updated to
 325          * its limit; its limit will not have been changed. The returned
 326          * output buffer's position will be zero and its limit will be the
 327          * number of resulting encoded bytes.
 328          *
 329          * @param   buffer
 330          *          the source ByteBuffer to encode
 331          * @return  A newly-allocated byte buffer containing the encoded bytes.
 332          */
 333         public ByteBuffer encode(ByteBuffer buffer) {
 334             int len = outLength(buffer.remaining());
 335             byte[] dst = new byte[len];
 336             int ret = 0;
 337             if (buffer.hasArray()) {
 338                 ret = encode0(buffer.array(),
 339                               buffer.arrayOffset() + buffer.position(),
 340                               buffer.arrayOffset() + buffer.limit(),
 341                               dst);
 342                 buffer.position(buffer.limit());
 343             } else {
 344                 byte[] src = new byte[buffer.remaining()];
 345                 buffer.get(src);
 346                 ret = encode0(src, 0, src.length, dst);
 347             }
 348             if (ret != dst.length)
 349                  dst = Arrays.copyOf(dst, ret);
 350             return ByteBuffer.wrap(dst);
 351         }
 352 
 353         /**
 354          * Encodes as many bytes as possible from the input byte buffer
 355          * using the {@link Base64} encoding scheme, writing the resulting
 356          * bytes to the given output byte buffer.
 357          *
 358          * <p>The buffers are read from, and written to, starting at their
 359          * current positions. Upon return, the input and output buffers'
 360          * positions will be advanced to reflect the bytes read and written,
 361          * but their limits will not be modified.
 362          *
 363          * <p>The encoding operation will stop and return if either all
 364          * remaining bytes in the input buffer have been encoded and written
 365          * to the output buffer, or the output buffer has insufficient space
 366          * to encode any more input bytes. The encoding operation can be
 367          * continued, if there is more bytes in input buffer to be encoded,
 368          * by invoking this method again with an output buffer that has more
 369          * {@linkplain java.nio.Buffer#remaining remaining} bytes. This is
 370          * typically done by draining any encoded bytes from the output buffer.
 371          * The value returned from last invocation needs to be passed in as the
 372          * third parameter {@code bytesOut} if it is to continue an unfinished
 373          * encoding, 0 otherwise.
 374          *
 375          * <p><b>Recommended Usage Example</b>
 376          * <pre>
 377          *    ByteBuffer src = ...;
 378          *    ByteBuffer dst = ...;
 379          *    Base64.Encoder enc = Base64.getMimeDecoder();
 380          *
 381          *    int bytesOut = 0;
 382          *    while (src.hasRemaining()) {
 383          *        // clear output buffer for decoding
 384          *        dst.clear();
 385          *        bytesOut = enc.encode(src, dst, bytesOut);
 386          *
 387          *        // read encoded bytes out of "dst"
 388          *        dst.flip();
 389          *        ...
 390          *    }
 391          * </pre>
 392          *
 393          * @param   src
 394          *          the input byte buffer to encode
 395          * @param   dst
 396          *          the output byte buffer
 397          * @param   bytesOut
 398          *          the return value of last invocation if this is to continue
 399          *          an unfinished encoding operation, 0 otherwise
 400          * @return  The sum total of {@code bytesOut} and the number of bytes
 401          *          written to the output ByteBuffer during this invocation.
 402          */
 403         public int encode(ByteBuffer src, ByteBuffer dst, int bytesOut) {
 404             if (src.hasArray() && dst.hasArray())
 405                 return encodeArray(src, dst, bytesOut);
 406             return encodeBuffer(src, dst, bytesOut);
 407         }
 408 
 409         /**
 410          * Wraps an output stream for encoding byte data using the {@link Base64}
 411          * encoding scheme.
 412          *
 413          * <p> It is recommended to promptly close the returned output stream after
 414          * use, during which it will flush all possible leftover bytes to the underlying
 415          * output stream. Closing the returned output stream will close the underlying
 416          * output stream.
 417          *
 418          * @param   os
 419          *          the output stream.
 420          * @return  the output stream for encoding the byte data into the
 421          *          specified Base64 encoded format
 422          */
 423         public OutputStream wrap(OutputStream os) {
 424             Objects.requireNonNull(os);
 425             return new EncOutputStream(os, isURL ? toBase64URL : toBase64,
 426                                        newline, linemax, doPadding);
 427         }
 428 
 429         /**
 430          * Returns an encoder instance that encodes equivalently to this one,
 431          * but without adding any padding character at the end of the encoded
 432          * byte data.
 433          *
 434          * <p> The encoding scheme of this encoder instance is unaffected by
 435          * this invocation. The returned encoder instance should be used for
 436          * non-padding encoding operation.
 437          *
 438          * @return an equivalent encoder that encodes without adding any
 439          *         padding character at the end
 440          */
 441         public Encoder withoutPadding() {
 442             if (!doPadding)
 443                 return this;
 444             return new Encoder(isURL, newline, linemax, false);
 445         }
 446 
 447         private int encodeArray(ByteBuffer src, ByteBuffer dst, int bytesOut) {
 448             char[] base64 = isURL? toBase64URL : toBase64;
 449             byte[] sa = src.array();
 450             int    sp = src.arrayOffset() + src.position();
 451             int    sl = src.arrayOffset() + src.limit();
 452             byte[] da = dst.array();
 453             int    dp = dst.arrayOffset() + dst.position();
 454             int    dl = dst.arrayOffset() + dst.limit();
 455             int    dp00 = dp;
 456             int    dpos = 0;        // dp of each line
 457             if (linemax > 0 && bytesOut > 0)
 458                 dpos = bytesOut % (linemax + newline.length);
 459             try {
 460                 if (dpos == linemax && sp < src.limit()) {
 461                     if (dp + newline.length > dl)
 462                         return  dp - dp00 + bytesOut;
 463                     for (byte b : newline){
 464                         dst.put(dp++, b);
 465                     }
 466                     dpos = 0;
 467                 }
 468                 sl = sp + (sl - sp) / 3 * 3;
 469                 while (sp < sl) {
 470                     int slen = (linemax > 0) ? (linemax - dpos) / 4 * 3
 471                                              : sl - sp;
 472                     int sl0 = Math.min(sp + slen, sl);
 473                     for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
 474                         if (dp0 + 4 > dl) {
 475                             sp = sp0; dp = dp0;
 476                             return  dp0 - dp00 + bytesOut;
 477                         }
 478                         int bits = (sa[sp0++] & 0xff) << 16 |
 479                                    (sa[sp0++] & 0xff) <<  8 |
 480                                    (sa[sp0++] & 0xff);
 481                         da[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
 482                         da[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
 483                         da[dp0++] = (byte)base64[(bits >>> 6)  & 0x3f];
 484                         da[dp0++] = (byte)base64[bits & 0x3f];
 485                     }
 486                     int n = (sl0 - sp) / 3 * 4;
 487                     dpos += n;
 488                     dp += n;
 489                     sp = sl0;
 490                     if (dpos == linemax && sp < src.limit()) {
 491                         if (dp + newline.length > dl)
 492                             return  dp - dp00 + bytesOut;
 493                         for (byte b : newline){
 494                             da[dp++] = b;
 495                         }
 496                         dpos = 0;
 497                     }
 498                 }
 499                 sl = src.arrayOffset() + src.limit();
 500                 if (sp < sl && dl >= dp + 4) {       // 1 or 2 leftover bytes
 501                     int b0 = sa[sp++] & 0xff;
 502                     da[dp++] = (byte)base64[b0 >> 2];
 503                     if (sp == sl) {
 504                         da[dp++] = (byte)base64[(b0 << 4) & 0x3f];
 505                         if (doPadding) {
 506                             da[dp++] = '=';
 507                             da[dp++] = '=';
 508                         }
 509                     } else {
 510                         int b1 = sa[sp++] & 0xff;
 511                         da[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
 512                         da[dp++] = (byte)base64[(b1 << 2) & 0x3f];
 513                         if (doPadding) {
 514                             da[dp++] = '=';
 515                         }
 516                     }
 517                 }
 518                 return dp - dp00 + bytesOut;
 519             } finally {
 520                 src.position(sp - src.arrayOffset());
 521                 dst.position(dp - dst.arrayOffset());
 522             }
 523         }
 524 
 525         private int encodeBuffer(ByteBuffer src, ByteBuffer dst, int bytesOut) {
 526             char[] base64 = isURL? toBase64URL : toBase64;
 527             int sp = src.position();
 528             int sl = src.limit();
 529             int dp = dst.position();
 530             int dl = dst.limit();
 531             int dp00 = dp;
 532 
 533             int dpos = 0;        // dp of each line
 534             if (linemax > 0 && bytesOut > 0)
 535                 dpos = bytesOut % (linemax + newline.length);
 536             try {
 537                 if (dpos == linemax && sp < src.limit()) {
 538                     if (dp + newline.length > dl)
 539                         return  dp - dp00 + bytesOut;
 540                     for (byte b : newline){
 541                         dst.put(dp++, b);
 542                     }
 543                     dpos = 0;
 544                 }
 545                 sl = sp + (sl - sp) / 3 * 3;
 546                 while (sp < sl) {
 547                     int slen = (linemax > 0) ? (linemax - dpos) / 4 * 3
 548                                              : sl - sp;
 549                     int sl0 = Math.min(sp + slen, sl);
 550                     for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
 551                         if (dp0 + 4 > dl) {
 552                             sp = sp0; dp = dp0;
 553                             return  dp0 - dp00 + bytesOut;
 554                         }
 555                         int bits = (src.get(sp0++) & 0xff) << 16 |
 556                                    (src.get(sp0++) & 0xff) <<  8 |
 557                                    (src.get(sp0++) & 0xff);
 558                         dst.put(dp0++, (byte)base64[(bits >>> 18) & 0x3f]);
 559                         dst.put(dp0++, (byte)base64[(bits >>> 12) & 0x3f]);
 560                         dst.put(dp0++, (byte)base64[(bits >>> 6)  & 0x3f]);
 561                         dst.put(dp0++, (byte)base64[bits & 0x3f]);
 562                     }
 563                     int n = (sl0 - sp) / 3 * 4;
 564                     dpos += n;
 565                     dp += n;
 566                     sp = sl0;
 567                     if (dpos == linemax && sp < src.limit()) {
 568                         if (dp + newline.length > dl)
 569                             return  dp - dp00 + bytesOut;
 570                         for (byte b : newline){
 571                             dst.put(dp++, b);
 572                         }
 573                         dpos = 0;
 574                     }
 575                 }
 576                 if (sp < src.limit() && dl >= dp + 4) {       // 1 or 2 leftover bytes
 577                     int b0 = src.get(sp++) & 0xff;
 578                     dst.put(dp++, (byte)base64[b0 >> 2]);
 579                     if (sp == src.limit()) {
 580                         dst.put(dp++, (byte)base64[(b0 << 4) & 0x3f]);
 581                         if (doPadding) {
 582                             dst.put(dp++, (byte)'=');
 583                             dst.put(dp++, (byte)'=');
 584                         }
 585                     } else {
 586                         int b1 = src.get(sp++) & 0xff;
 587                         dst.put(dp++, (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
 588                         dst.put(dp++, (byte)base64[(b1 << 2) & 0x3f]);
 589                         if (doPadding) {
 590                             dst.put(dp++, (byte)'=');
 591                         }
 592                     }
 593                 }
 594                 return dp - dp00 + bytesOut;
 595             } finally {
 596                 src.position(sp);
 597                 dst.position(dp);
 598             }
 599         }
 600 
 601         private int encode0(byte[] src, int off, int end, byte[] dst) {
 602             char[] base64 = isURL ? toBase64URL : toBase64;
 603             int sp = off;
 604             int slen = (end - off) / 3 * 3;
 605             int sl = off + slen;
 606             if (linemax > 0 && slen  > linemax / 4 * 3)
 607                 slen = linemax / 4 * 3;
 608             int dp = 0;
 609             while (sp < sl) {
 610                 int sl0 = Math.min(sp + slen, sl);
 611                 for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
 612                     int bits = (src[sp0++] & 0xff) << 16 |
 613                                (src[sp0++] & 0xff) <<  8 |
 614                                (src[sp0++] & 0xff);
 615                     dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
 616                     dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
 617                     dst[dp0++] = (byte)base64[(bits >>> 6)  & 0x3f];
 618                     dst[dp0++] = (byte)base64[bits & 0x3f];
 619                 }
 620                 int dlen = (sl0 - sp) / 3 * 4;
 621                 dp += dlen;
 622                 sp = sl0;
 623                 if (dlen == linemax && sp < end) {
 624                     for (byte b : newline){
 625                         dst[dp++] = b;
 626                     }
 627                 }
 628             }
 629             if (sp < end) {               // 1 or 2 leftover bytes
 630                 int b0 = src[sp++] & 0xff;
 631                 dst[dp++] = (byte)base64[b0 >> 2];
 632                 if (sp == end) {
 633                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
 634                     if (doPadding) {
 635                         dst[dp++] = '=';
 636                         dst[dp++] = '=';
 637                     }
 638                 } else {
 639                     int b1 = src[sp++] & 0xff;
 640                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
 641                     dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
 642                     if (doPadding) {
 643                         dst[dp++] = '=';
 644                     }
 645                 }
 646             }
 647             return dp;
 648         }
 649     }
 650 
 651     /**
 652      * This class implements a decoder for decoding byte data using the
 653      * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
 654      *
 655      * <p> The Base64 padding character {@code '='} is accepted and
 656      * interpreted as the end of the encoded byte data, but is not
 657      * required. So if the final unit of the encoded byte data only has
 658      * two or three Base64 characters (without the corresponding padding
 659      * character(s) padded), they are decoded as if followed by padding
 660      * character(s).
 661      * <p>
 662      * For decoders that use the <a href="#basic">Basic</a> and
 663      * <a href="#url">URL and Filename safe</a> type base64 scheme, and
 664      * if there is padding character present in the final unit, the
 665      * correct number of padding character(s) must be present, otherwise
 666      * {@code IllegalArgumentException} ({@code IOException} when reading
 667      * from a Base64 stream) is thrown during decoding.
 668      * <p>
 669      * Decoders that use the <a href="#mime">MIME</a> type base64 scheme
 670      * are more lenient when decoding the padding character(s). If the
 671      * padding character(s) is incorrectly encoded, the first padding
 672      * character encountered is interpreted as the end of the encoded byte
 673      * data, the decoding operation will then end and return normally.
 674      *
 675      * <p> Instances of {@link Decoder} class are safe for use by
 676      * multiple concurrent threads.
 677      *
 678      * <p> Unless otherwise noted, passing a {@code null} argument to
 679      * a method of this class will cause a
 680      * {@link java.lang.NullPointerException NullPointerException} to
 681      * be thrown.
 682      *
 683      * @see     Encoder
 684      * @since   1.8
 685      */
 686     public static class Decoder {
 687 
 688         private final boolean isURL;
 689         private final boolean isMIME;
 690 
 691         private Decoder(boolean isURL, boolean isMIME) {
 692             this.isURL = isURL;
 693             this.isMIME = isMIME;
 694         }
 695 
 696         /**
 697          * Lookup table for decoding unicode characters drawn from the
 698          * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into
 699          * their 6-bit positive integer equivalents.  Characters that
 700          * are not in the Base64 alphabet but fall within the bounds of
 701          * the array are encoded to -1.
 702          *
 703          */
 704         private static final int[] fromBase64 = new int[256];
 705         static {
 706             Arrays.fill(fromBase64, -1);
 707             for (int i = 0; i < Encoder.toBase64.length; i++)
 708                 fromBase64[Encoder.toBase64[i]] = i;
 709             fromBase64['='] = -2;
 710         }
 711 
 712         /**
 713          * Lookup table for decoding "URL and Filename safe Base64 Alphabet"
 714          * as specified in Table2 of the RFC 4648.
 715          */
 716         private static final int[] fromBase64URL = new int[256];
 717 
 718         static {
 719             Arrays.fill(fromBase64URL, -1);
 720             for (int i = 0; i < Encoder.toBase64URL.length; i++)
 721                 fromBase64URL[Encoder.toBase64URL[i]] = i;
 722             fromBase64URL['='] = -2;
 723         }
 724 
 725         static final Decoder RFC4648         = new Decoder(false, false);
 726         static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
 727         static final Decoder RFC2045         = new Decoder(false, true);
 728 
 729         /**
 730          * Decodes all bytes from the input byte array using the {@link Base64}
 731          * encoding scheme, writing the results into a newly-allocated output
 732          * byte array. The returned byte array is of the length of the resulting
 733          * bytes.
 734          *
 735          * @param   src
 736          *          the byte array to decode
 737          *
 738          * @return  A newly-allocated byte array containing the decoded bytes.
 739          *
 740          * @throws  IllegalArgumentException
 741          *          if {@code src} is not in valid Base64 scheme
 742          */
 743         public byte[] decode(byte[] src) {
 744             byte[] dst = new byte[outLength(src, 0, src.length)];
 745             int ret = decode0(src, 0, src.length, dst);
 746             if (ret != dst.length) {
 747                 dst = Arrays.copyOf(dst, ret);
 748             }
 749             return dst;
 750         }
 751 
 752         /**
 753          * Decodes a Base64 encoded String into a newly-allocated byte array
 754          * using the {@link Base64} encoding scheme.
 755          *
 756          * <p> An invocation of this method has exactly the same effect as invoking
 757          * {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))}
 758          *
 759          * @param   src
 760          *          the string to decode
 761          *
 762          * @return  A newly-allocated byte array containing the decoded bytes.
 763          *
 764          * @throws  IllegalArgumentException
 765          *          if {@code src} is not in valid Base64 scheme
 766          */
 767         public byte[] decode(String src) {
 768             return decode(src.getBytes(StandardCharsets.ISO_8859_1));
 769         }
 770 
 771         /**
 772          * Decodes all bytes from the input byte array using the {@link Base64}
 773          * encoding scheme, writing the results into the given output byte array,
 774          * starting at offset 0.
 775          *
 776          * <p> It is the responsibility of the invoker of this method to make
 777          * sure the output byte array {@code dst} has enough space for decoding
 778          * all bytes from the input byte array. No bytes will be be written to
 779          * the output byte array if the output byte array is not big enough.
 780          *
 781          * <p> If the input byte array is not in valid Base64 encoding scheme
 782          * then some bytes may have been written to the output byte array before
 783          * IllegalargumentException is thrown.
 784          *
 785          * @param   src
 786          *          the byte array to decode
 787          * @param   dst
 788          *          the output byte array
 789          *
 790          * @return  The number of bytes written to the output byte array
 791          *
 792          * @throws  IllegalArgumentException
 793          *          if {@code src} is not in valid Base64 scheme, or {@code dst}
 794          *          does not have enough space for decoding all input bytes.
 795          */
 796         public int decode(byte[] src, byte[] dst) {
 797             int len = outLength(src, 0, src.length);
 798             if (dst.length < len)
 799                 throw new IllegalArgumentException(
 800                     "Output byte array is too small for decoding all input bytes");
 801             return decode0(src, 0, src.length, dst);
 802         }
 803 
 804         /**
 805          * Decodes all bytes from the input byte buffer using the {@link Base64}
 806          * encoding scheme, writing the results into a newly-allocated ByteBuffer.
 807          *
 808          * <p> Upon return, the source buffer's position will be updated to
 809          * its limit; its limit will not have been changed. The returned
 810          * output buffer's position will be zero and its limit will be the
 811          * number of resulting decoded bytes
 812          *
 813          * @param   buffer
 814          *          the ByteBuffer to decode
 815          *
 816          * @return  A newly-allocated byte buffer containing the decoded bytes
 817          *
 818          * @throws  IllegalArgumentException
 819          *          if {@code src} is not in valid Base64 scheme.
 820          */
 821         public ByteBuffer decode(ByteBuffer buffer) {
 822             int pos0 = buffer.position();
 823             try {
 824                 byte[] src;
 825                 int sp, sl;
 826                 if (buffer.hasArray()) {
 827                     src = buffer.array();
 828                     sp = buffer.arrayOffset() + buffer.position();
 829                     sl = buffer.arrayOffset() + buffer.limit();
 830                     buffer.position(buffer.limit());
 831                 } else {
 832                     src = new byte[buffer.remaining()];
 833                     buffer.get(src);
 834                     sp = 0;
 835                     sl = src.length;
 836                 }
 837                 byte[] dst = new byte[outLength(src, sp, sl)];
 838                 return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst));
 839             } catch (IllegalArgumentException iae) {
 840                 buffer.position(pos0);
 841                 throw iae;
 842             }
 843         }
 844 
 845         /**
 846          * Decodes as many bytes as possible from the input byte buffer
 847          * using the {@link Base64} encoding scheme, writing the resulting
 848          * bytes to the given output byte buffer.
 849          *
 850          * <p>The buffers are read from, and written to, starting at their
 851          * current positions. Upon return, the input and output buffers'
 852          * positions will be advanced to reflect the bytes read and written,
 853          * but their limits will not be modified.
 854          *
 855          * <p> If the input buffer is not in valid Base64 encoding scheme
 856          * then some bytes may have been written to the output buffer
 857          * before IllegalArgumentException is thrown. The positions of
 858          * both input and output buffer will not be advanced in this case.
 859          *
 860          * <p>The decoding operation will end and return if all remaining
 861          * bytes in the input buffer have been decoded and written to the
 862          * output buffer.
 863          *
 864          * <p> The decoding operation will stop and return if the output
 865          * buffer has insufficient space to decode any more input bytes.
 866          * The decoding operation can be continued, if there is more bytes
 867          * in input buffer to be decoded, by invoking this method again with
 868          * an output buffer that has more {@linkplain java.nio.Buffer#remaining
 869          * remaining} bytes. This is typically done by draining any decoded
 870          * bytes from the output buffer.
 871          *
 872          * <p><b>Recommended Usage Example</b>
 873          * <pre>
 874          *    ByteBuffer src = ...;
 875          *    ByteBuffer dst = ...;
 876          *    Base64.Decoder dec = Base64.getDecoder();
 877          *
 878          *    while (src.hasRemaining()) {
 879          *
 880          *        // prepare the output byte buffer
 881          *        dst.clear();
 882          *        dec.decode(src, dst);
 883          *
 884          *        // read bytes from the output buffer
 885          *        dst.flip();
 886          *        ...
 887          *    }
 888          * </pre>
 889          *
 890          * @param   src
 891          *          the input byte buffer to decode
 892          * @param   dst
 893          *          the output byte buffer
 894          *
 895          * @return  The number of bytes written to the output byte buffer during
 896          *          this decoding invocation
 897          *
 898          * @throws  IllegalArgumentException
 899          *          if {@code src} is not in valid Base64 scheme.
 900          */
 901         public int decode(ByteBuffer src, ByteBuffer dst) {
 902             int sp0 = src.position();
 903             int dp0 = dst.position();
 904             try {
 905                 if (src.hasArray() && dst.hasArray())
 906                     return decodeArray(src, dst);
 907                 return decodeBuffer(src, dst);
 908             } catch (IllegalArgumentException iae) {
 909                 src.position(sp0);
 910                 dst.position(dp0);
 911                 throw iae;
 912             }
 913         }
 914 
 915         /**
 916          * Returns an input stream for decoding {@link Base64} encoded byte stream.
 917          *
 918          * <p> The {@code read}  methods of the returned {@code InputStream} will
 919          * throw {@code IOException} when reading bytes that cannot be decoded.
 920          *
 921          * <p> Closing the returned input stream will close the underlying
 922          * input stream.
 923          *
 924          * @param   is
 925          *          the input stream
 926          *
 927          * @return  the input stream for decoding the specified Base64 encoded
 928          *          byte stream
 929          */
 930         public InputStream wrap(InputStream is) {
 931             Objects.requireNonNull(is);
 932             return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
 933         }
 934 
 935         private int decodeArray(ByteBuffer src, ByteBuffer dst) {
 936             int[] base64 = isURL ? fromBase64URL : fromBase64;
 937             int   bits = 0;
 938             int   shiftto = 18;       // pos of first byte of 4-byte atom
 939             byte[] sa = src.array();
 940             int    sp = src.arrayOffset() + src.position();
 941             int    sl = src.arrayOffset() + src.limit();
 942             byte[] da = dst.array();
 943             int    dp = dst.arrayOffset() + dst.position();
 944             int    dl = dst.arrayOffset() + dst.limit();
 945             int    dp0 = dp;
 946             int    mark = sp;
 947             try {
 948                 while (sp < sl) {
 949                     int b = sa[sp++] & 0xff;
 950                     if ((b = base64[b]) < 0) {
 951                         if (b == -2) {   // padding byte
 952                             if (!isMIME &&
 953                                 (shiftto == 6 && (sp == sl || sa[sp++] != '=') ||
 954                                  shiftto == 18)) {
 955                                 throw new IllegalArgumentException(
 956                                      "Input byte array has wrong 4-byte ending unit");
 957                             }
 958                             break;
 959                         }
 960                         if (isMIME)     // skip if for rfc2045
 961                             continue;
 962                         else
 963                             throw new IllegalArgumentException(
 964                                 "Illegal base64 character " +
 965                                 Integer.toString(sa[sp - 1], 16));
 966                     }
 967                     bits |= (b << shiftto);
 968                     shiftto -= 6;
 969                     if (shiftto < 0) {
 970                         if (dl < dp + 3)
 971                             return dp - dp0;
 972                         da[dp++] = (byte)(bits >> 16);
 973                         da[dp++] = (byte)(bits >>  8);
 974                         da[dp++] = (byte)(bits);
 975                         shiftto = 18;
 976                         bits = 0;
 977                         mark = sp;
 978                     }
 979                 }
 980                 if (shiftto == 6) {
 981                     if (dl - dp < 1)
 982                         return dp - dp0;
 983                     da[dp++] = (byte)(bits >> 16);
 984                 } else if (shiftto == 0) {
 985                     if (dl - dp < 2)
 986                         return dp - dp0;
 987                     da[dp++] = (byte)(bits >> 16);
 988                     da[dp++] = (byte)(bits >>  8);
 989                 } else if (shiftto == 12) {
 990                     throw new IllegalArgumentException(
 991                         "Last unit does not have enough valid bits");
 992                 }
 993                 if (sp < sl) {
 994                     if (isMIME)
 995                         sp = sl;
 996                     else
 997                         throw new IllegalArgumentException(
 998                             "Input byte array has incorrect ending byte at " + sp);
 999                 }
1000                 mark = sp;
1001                 return dp - dp0;
1002             } finally {
1003                 src.position(mark);
1004                 dst.position(dp);
1005             }
1006         }
1007 
1008         private int decodeBuffer(ByteBuffer src, ByteBuffer dst) {
1009             int[] base64 = isURL ? fromBase64URL : fromBase64;
1010             int   bits = 0;
1011             int   shiftto = 18;       // pos of first byte of 4-byte atom
1012             int    sp = src.position();
1013             int    sl = src.limit();
1014             int    dp = dst.position();
1015             int    dl = dst.limit();
1016             int    dp0 = dp;
1017             int    mark = sp;
1018             try {
1019                 while (sp < sl) {
1020                     int b = src.get(sp++) & 0xff;
1021                     if ((b = base64[b]) < 0) {
1022                         if (b == -2) {  // padding byte
1023                             if (!isMIME &&
1024                                 (shiftto == 6 && (sp == sl || src.get(sp++) != '=') ||
1025                                  shiftto == 18)) {
1026                                 throw new IllegalArgumentException(
1027                                      "Input byte array has wrong 4-byte ending unit");
1028                             }
1029                             break;
1030                         }
1031                         if (isMIME)     // skip if for rfc2045
1032                             continue;
1033                         else
1034                             throw new IllegalArgumentException(
1035                                 "Illegal base64 character " +
1036                                 Integer.toString(src.get(sp - 1), 16));
1037                     }
1038                     bits |= (b << shiftto);
1039                     shiftto -= 6;
1040                     if (shiftto < 0) {
1041                         if (dl < dp + 3)
1042                             return dp - dp0;
1043                         dst.put(dp++, (byte)(bits >> 16));
1044                         dst.put(dp++, (byte)(bits >>  8));
1045                         dst.put(dp++, (byte)(bits));
1046                         shiftto = 18;
1047                         bits = 0;
1048                         mark = sp;
1049                     }
1050                 }
1051                 if (shiftto == 6) {
1052                     if (dl - dp < 1)
1053                         return dp - dp0;
1054                      dst.put(dp++, (byte)(bits >> 16));
1055                 } else if (shiftto == 0) {
1056                     if (dl - dp < 2)
1057                         return dp - dp0;
1058                     dst.put(dp++, (byte)(bits >> 16));
1059                     dst.put(dp++, (byte)(bits >>  8));
1060                 } else if (shiftto == 12) {
1061                     throw new IllegalArgumentException(
1062                         "Last unit does not have enough valid bits");
1063                 }
1064                 if (sp < sl) {
1065                     if (isMIME)
1066                         sp = sl;
1067                     else
1068                         throw new IllegalArgumentException(
1069                             "Input byte array has incorrect ending byte at " + sp);
1070                 }
1071                 mark = sp;
1072                 return dp - dp0;
1073             } finally {
1074                 src.position(mark);
1075                 dst.position(dp);
1076             }
1077         }
1078 
1079         private int outLength(byte[] src, int sp, int sl) {
1080             int[] base64 = isURL ? fromBase64URL : fromBase64;
1081             int paddings = 0;
1082             int len = sl - sp;
1083             if (len == 0)
1084                 return 0;
1085             if (len < 2) {
1086                 if (isMIME && base64[0] == -1)
1087                     return 0;
1088                 throw new IllegalArgumentException(
1089                     "Input byte[] should at least have 2 bytes for base64 bytes");
1090             }
1091             if (isMIME) {
1092                 // scan all bytes to fill out all non-alphabet. a performance
1093                 // trade-off of pre-scan or Arrays.copyOf
1094                 int n = 0;
1095                 while (sp < sl) {
1096                     int b = src[sp++] & 0xff;
1097                     if (b == '=') {
1098                         len -= (sl - sp + 1);
1099                         break;
1100                     }
1101                     if ((b = base64[b]) == -1)
1102                         n++;
1103                 }
1104                 len -= n;
1105             } else {
1106                 if (src[sl - 1] == '=') {
1107                     paddings++;
1108                     if (src[sl - 2] == '=')
1109                         paddings++;
1110                 }
1111             }
1112             if (paddings == 0 && (len & 0x3) !=  0)
1113                 paddings = 4 - (len & 0x3);
1114             return 3 * ((len + 3) / 4) - paddings;
1115         }
1116 
1117         private int decode0(byte[] src, int sp, int sl, byte[] dst) {
1118             int[] base64 = isURL ? fromBase64URL : fromBase64;
1119             int dp = 0;
1120             int bits = 0;
1121             int shiftto = 18;       // pos of first byte of 4-byte atom
1122             while (sp < sl) {
1123                 int b = src[sp++] & 0xff;
1124                 if ((b = base64[b]) < 0) {
1125                     if (b == -2) {         // padding byte '='
1126                         if (!isMIME  &&    // be lenient for rfc2045
1127                             // =     shiftto==18 unnecessary padding
1128                             // x=    shiftto==12 a dangling single x
1129                             // x     to be handled together with non-padding case
1130                             // xx=   shiftto==6&&sp==sl missing last =
1131                             // xx=y  shiftto==6 last is not =
1132                             (shiftto == 6 && (sp == sl || src[sp++] != '=') ||
1133                             shiftto == 18)) {
1134                             throw new IllegalArgumentException(
1135                                 "Input byte array has wrong 4-byte ending unit");
1136                         }
1137                         break;
1138                     }
1139                     if (isMIME)    // skip if for rfc2045
1140                         continue;
1141                     else
1142                         throw new IllegalArgumentException(
1143                             "Illegal base64 character " +
1144                             Integer.toString(src[sp - 1], 16));
1145                 }
1146                 bits |= (b << shiftto);
1147                 shiftto -= 6;
1148                 if (shiftto < 0) {
1149                     dst[dp++] = (byte)(bits >> 16);
1150                     dst[dp++] = (byte)(bits >>  8);
1151                     dst[dp++] = (byte)(bits);
1152                     shiftto = 18;
1153                     bits = 0;
1154                 }
1155             }
1156             // reached end of byte array or hit padding '=' characters.
1157             if (shiftto == 6) {
1158                 dst[dp++] = (byte)(bits >> 16);
1159             } else if (shiftto == 0) {
1160                 dst[dp++] = (byte)(bits >> 16);
1161                 dst[dp++] = (byte)(bits >>  8);
1162             } else if (shiftto == 12) {
1163                 // dangling single "x", throw exception even in lenient mode,
1164                 // it's incorrectly encoded.
1165                 throw new IllegalArgumentException(
1166                     "Last unit does not have enough valid bits");
1167             }
1168             // anything left is invalid, if is not MIME.
1169             // if MIME (lenient), just ignore all leftover
1170             if (sp < sl && !isMIME) {
1171                 throw new IllegalArgumentException(
1172                     "Input byte array has incorrect ending byte at " + sp);
1173             }
1174             return dp;
1175         }
1176     }
1177 
1178     /*
1179      * An output stream for encoding bytes into the Base64.
1180      */
1181     private static class EncOutputStream extends FilterOutputStream {
1182 
1183         private int leftover = 0;
1184         private int b0, b1, b2;
1185         private boolean closed = false;
1186 
1187         private final char[] base64;    // byte->base64 mapping
1188         private final byte[] newline;   // line separator, if needed
1189         private final int linemax;
1190         private final boolean doPadding;// whether or not to pad
1191         private int linepos = 0;
1192 
1193         EncOutputStream(OutputStream os, char[] base64,
1194                         byte[] newline, int linemax, boolean doPadding) {
1195             super(os);
1196             this.base64 = base64;
1197             this.newline = newline;
1198             this.linemax = linemax;
1199             this.doPadding = doPadding;
1200         }
1201 
1202         @Override
1203         public void write(int b) throws IOException {
1204             byte[] buf = new byte[1];
1205             buf[0] = (byte)(b & 0xff);
1206             write(buf, 0, 1);
1207         }
1208 
1209         private void checkNewline() throws IOException {
1210             if (linepos == linemax) {
1211                 out.write(newline);
1212                 linepos = 0;
1213             }
1214         }
1215 
1216         @Override
1217         public void write(byte[] b, int off, int len) throws IOException {
1218             if (closed)
1219                 throw new IOException("Stream is closed");
1220             if (off < 0 || len < 0 || off + len > b.length)
1221                 throw new ArrayIndexOutOfBoundsException();
1222             if (len == 0)
1223                 return;
1224             if (leftover != 0) {
1225                 if (leftover == 1) {
1226                     b1 = b[off++] & 0xff;
1227                     len--;
1228                     if (len == 0) {
1229                         leftover++;
1230                         return;
1231                     }
1232                 }
1233                 b2 = b[off++] & 0xff;
1234                 len--;
1235                 checkNewline();
1236                 out.write(base64[b0 >> 2]);
1237                 out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
1238                 out.write(base64[(b1 << 2) & 0x3f | (b2 >> 6)]);
1239                 out.write(base64[b2 & 0x3f]);
1240                 linepos += 4;
1241             }
1242             int nBits24 = len / 3;
1243             leftover = len - (nBits24 * 3);
1244             while (nBits24-- > 0) {
1245                 checkNewline();
1246                 int bits = (b[off++] & 0xff) << 16 |
1247                            (b[off++] & 0xff) <<  8 |
1248                            (b[off++] & 0xff);
1249                 out.write(base64[(bits >>> 18) & 0x3f]);
1250                 out.write(base64[(bits >>> 12) & 0x3f]);
1251                 out.write(base64[(bits >>> 6)  & 0x3f]);
1252                 out.write(base64[bits & 0x3f]);
1253                 linepos += 4;
1254            }
1255             if (leftover == 1) {
1256                 b0 = b[off++] & 0xff;
1257             } else if (leftover == 2) {
1258                 b0 = b[off++] & 0xff;
1259                 b1 = b[off++] & 0xff;
1260             }
1261         }
1262 
1263         @Override
1264         public void close() throws IOException {
1265             if (!closed) {
1266                 closed = true;
1267                 if (leftover == 1) {
1268                     checkNewline();
1269                     out.write(base64[b0 >> 2]);
1270                     out.write(base64[(b0 << 4) & 0x3f]);
1271                     if (doPadding) {
1272                         out.write('=');
1273                         out.write('=');
1274                     }
1275                 } else if (leftover == 2) {
1276                     checkNewline();
1277                     out.write(base64[b0 >> 2]);
1278                     out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
1279                     out.write(base64[(b1 << 2) & 0x3f]);
1280                     if (doPadding) {
1281                        out.write('=');
1282                     }
1283                 }
1284                 leftover = 0;
1285                 out.close();
1286             }
1287         }
1288     }
1289 
1290     /*
1291      * An input stream for decoding Base64 bytes
1292      */
1293     private static class DecInputStream extends InputStream {
1294 
1295         private final InputStream is;
1296         private final boolean isMIME;
1297         private final int[] base64;      // base64 -> byte mapping
1298         private int bits = 0;            // 24-bit buffer for decoding
1299         private int nextin = 18;         // next available "off" in "bits" for input;
1300                                          // -> 18, 12, 6, 0
1301         private int nextout = -8;        // next available "off" in "bits" for output;
1302                                          // -> 8, 0, -8 (no byte for output)
1303         private boolean eof = false;
1304         private boolean closed = false;
1305 
1306         DecInputStream(InputStream is, int[] base64, boolean isMIME) {
1307             this.is = is;
1308             this.base64 = base64;
1309             this.isMIME = isMIME;
1310         }
1311 
1312         private byte[] sbBuf = new byte[1];
1313 
1314         @Override
1315         public int read() throws IOException {
1316             return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff;
1317         }
1318 
1319         @Override
1320         public int read(byte[] b, int off, int len) throws IOException {
1321             if (closed)
1322                 throw new IOException("Stream is closed");
1323             if (eof && nextout < 0)    // eof and no leftover
1324                 return -1;
1325             if (off < 0 || len < 0 || len > b.length - off)
1326                 throw new IndexOutOfBoundsException();
1327             int oldOff = off;
1328             if (nextout >= 0) {       // leftover output byte(s) in bits buf
1329                 do {
1330                     if (len == 0)
1331                         return off - oldOff;
1332                     b[off++] = (byte)(bits >> nextout);
1333                     len--;
1334                     nextout -= 8;
1335                 } while (nextout >= 0);
1336                 bits = 0;
1337             }
1338             while (len > 0) {
1339                 int v = is.read();
1340                 if (v == -1) {
1341                     eof = true;
1342                     if (nextin != 18) {
1343                         if (nextin == 12)
1344                             throw new IOException("Base64 stream has one un-decoded dangling byte.");
1345                         // treat ending xx/xxx without padding character legal.
1346                         // same logic as v == '=' below
1347                         b[off++] = (byte)(bits >> (16));
1348                         len--;
1349                         if (nextin == 0) {           // only one padding byte
1350                             if (len == 0) {          // no enough output space
1351                                 bits >>= 8;          // shift to lowest byte
1352                                 nextout = 0;
1353                             } else {
1354                                 b[off++] = (byte) (bits >>  8);
1355                             }
1356                         }
1357                     }
1358                     if (off == oldOff)
1359                         return -1;
1360                     else
1361                         return off - oldOff;
1362                 }
1363                 if (v == '=') {                  // padding byte(s)
1364                     // =     shiftto==18 unnecessary padding
1365                     // x=    shiftto==12 dangling x, invalid unit
1366                     // xx=   shiftto==6 && missing last '='
1367                     // xx=y  or last is not '='
1368                     if (nextin == 18 || nextin == 12 ||
1369                         nextin == 6 && is.read() != '=') {
1370                         if (!isMIME || nextin == 12) {
1371                             throw new IOException("Illegal base64 ending sequence:" + nextin);
1372                         } else if (nextin != 18) {
1373                             // lenient mode for mime
1374                             // (1) handle the "unnecessary padding in "xxxx ="
1375                             //     case as the eof (nextin == 18)
1376                             // (2) decode "xx=" and "xx=y" normally
1377                             b[off++] = (byte)(bits >> (16));
1378                             len--;
1379                         }
1380                     } else {
1381                         b[off++] = (byte)(bits >> (16));
1382                         len--;
1383                         if (nextin == 0) {           // only one padding byte
1384                             if (len == 0) {          // no enough output space
1385                                 bits >>= 8;          // shift to lowest byte
1386                                 nextout = 0;
1387                             } else {
1388                                 b[off++] = (byte) (bits >>  8);
1389                             }
1390                         }
1391                     }
1392                     eof = true;
1393                     break;
1394                 }
1395                 if ((v = base64[v]) == -1) {
1396                     if (isMIME)                 // skip if for rfc2045
1397                         continue;
1398                     else
1399                         throw new IOException("Illegal base64 character " +
1400                             Integer.toString(v, 16));
1401                 }
1402                 bits |= (v << nextin);
1403                 if (nextin == 0) {
1404                     nextin = 18;    // clear for next
1405                     nextout = 16;
1406                     while (nextout >= 0) {
1407                         b[off++] = (byte)(bits >> nextout);
1408                         len--;
1409                         nextout -= 8;
1410                         if (len == 0 && nextout >= 0) {  // don't clean "bits"
1411                             return off - oldOff;
1412                         }
1413                     }
1414                     bits = 0;
1415                 } else {
1416                     nextin -= 6;
1417                 }
1418             }
1419             return off - oldOff;
1420         }
1421 
1422         @Override
1423         public int available() throws IOException {
1424             if (closed)
1425                 throw new IOException("Stream is closed");
1426             return is.available();   // TBD:
1427         }
1428 
1429         @Override
1430         public void close() throws IOException {
1431             if (!closed) {
1432                 closed = true;
1433                 is.close();
1434             }
1435         }
1436     }
1437 }