src/share/classes/java/util/Base64.java

Print this page




  47  * <p> Uses "The Base64 Alphabet" as specified in Table 1 of
  48  *     RFC 4648 and RFC 2045 for encoding and decoding operation.
  49  *     The encoder does not add any line feed (line separator)
  50  *     character. The decoder rejects data that contains characters
  51  *     outside the base64 alphabet.</p></li>
  52  *
  53  * <a name="url">
  54  * <li><b>URL and Filename safe</b>
  55  * <p> Uses the "URL and Filename safe Base64 Alphabet" as specified
  56  *     in Table 2 of RFC 4648 for encoding and decoding. The
  57  *     encoder does not add any line feed (line separator) character.
  58  *     The decoder rejects data that contains characters outside the
  59  *     base64 alphabet.</p></li>
  60  *
  61  * <a name="mime">
  62  * <li><b>MIME</b>
  63  * <p> Uses the "The Base64 Alphabet" as specified in Table 1 of
  64  *     RFC 2045 for encoding and decoding operation. The encoded output
  65  *     must be represented in lines of no more than 76 characters each
  66  *     and uses a carriage return {@code '\r'} followed immediately by
  67  *     a linefeed {@code '\n'} as the line separator. All line separators

  68  *     or other characters not found in the base64 alphabet table are
  69  *     ignored in decoding operation.</p></li>
  70  * </ul>
  71  *
  72  * <p> Unless otherwise noted, passing a {@code null} argument to a
  73  * method of this class will cause a {@link java.lang.NullPointerException
  74  * NullPointerException} to be thrown.
  75  *
  76  * @author  Xueming Shen
  77  * @since   1.8
  78  */
  79 
  80 public class Base64 {
  81 
  82     private Base64() {}
  83 
  84     /**
  85      * Returns a {@link Encoder} that encodes using the
  86      * <a href="#basic">Basic</a> type base64 encoding scheme.
  87      *


 597                 dst[dp++] = (byte)base64[b0 >> 2];
 598                 if (sp == end) {
 599                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
 600                     dst[dp++] = '=';
 601                     dst[dp++] = '=';
 602                 } else {
 603                     int b1 = src[sp++] & 0xff;
 604                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
 605                     dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
 606                     dst[dp++] = '=';
 607                 }
 608             }
 609             return dp;
 610         }
 611     }
 612 
 613     /**
 614      * This class implements a decoder for decoding byte data using the
 615      * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
 616      *







 617      * <p> Instances of {@link Decoder} class are safe for use by
 618      * multiple concurrent threads.
 619      *
 620      * <p> Unless otherwise noted, passing a {@code null} argument to
 621      * a method of this class will cause a
 622      * {@link java.lang.NullPointerException NullPointerException} to
 623      * be thrown.
 624      *
 625      * @see     Encoder
 626      * @since   1.8
 627      */
 628     public static class Decoder {
 629 
 630         private final boolean isURL;
 631         private final boolean isMIME;
 632 
 633         private Decoder(boolean isURL, boolean isMIME) {
 634             this.isURL = isURL;
 635             this.isMIME = isMIME;
 636         }


 840          * @throws  IllegalArgumentException
 841          *          if {@code src} is not in valid Base64 scheme.
 842          */
 843         public int decode(ByteBuffer src, ByteBuffer dst) {
 844             int sp0 = src.position();
 845             int dp0 = dst.position();
 846             try {
 847                 if (src.hasArray() && dst.hasArray())
 848                     return decodeArray(src, dst);
 849                 return decodeBuffer(src, dst);
 850             } catch (IllegalArgumentException iae) {
 851                 src.position(sp0);
 852                 dst.position(dp0);
 853                 throw iae;
 854             }
 855         }
 856 
 857         /**
 858          * Returns an input stream for decoding {@link Base64} encoded byte stream.
 859          *



 860          * <p> Closing the returned input stream will close the underlying
 861          * input stream.
 862          *
 863          * @param   is
 864          *          the input stream
 865          *
 866          * @return  the input stream for decoding the specified Base64 encoded
 867          *          byte stream
 868          */
 869         public InputStream wrap(InputStream is) {
 870             Objects.requireNonNull(is);
 871             return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
 872         }
 873 
 874         private int decodeArray(ByteBuffer src, ByteBuffer dst) {
 875             int[] base64 = isURL ? fromBase64URL : fromBase64;
 876             int   bits = 0;
 877             int   shiftto = 18;       // pos of first byte of 4-byte atom
 878             byte[] sa = src.array();
 879             int    sp = src.arrayOffset() + src.position();
 880             int    sl = src.arrayOffset() + src.limit();
 881             byte[] da = dst.array();
 882             int    dp = dst.arrayOffset() + dst.position();
 883             int    dl = dst.arrayOffset() + dst.limit();
 884             int    dp0 = dp;
 885             int    mark = sp;
 886             boolean padding = false;
 887             try {
 888                 while (sp < sl) {
 889                     int b = sa[sp++] & 0xff;
 890                     if ((b = base64[b]) < 0) {
 891                         if (b == -2) {   // padding byte
 892                             padding = true;




 893                             break;
 894                         }
 895                         if (isMIME)     // skip if for rfc2045
 896                             continue;
 897                         else
 898                             throw new IllegalArgumentException(
 899                                 "Illegal base64 character " +
 900                                 Integer.toString(sa[sp - 1], 16));
 901                     }
 902                     bits |= (b << shiftto);
 903                     shiftto -= 6;
 904                     if (shiftto < 0) {
 905                         if (dl < dp + 3)
 906                             return dp - dp0;
 907                         da[dp++] = (byte)(bits >> 16);
 908                         da[dp++] = (byte)(bits >>  8);
 909                         da[dp++] = (byte)(bits);
 910                         shiftto = 18;
 911                         bits = 0;
 912                         mark = sp;
 913                     }
 914                 }
 915                 if (shiftto == 6) {
 916                     if (dl - dp < 1)
 917                         return dp - dp0;
 918                     if (padding && (sp + 1 != sl || sa[sp++] != '='))
 919                         throw new IllegalArgumentException(
 920                             "Input buffer has wrong 4-byte ending unit");
 921                     da[dp++] = (byte)(bits >> 16);
 922                     mark = sp;
 923                 } else if (shiftto == 0) {
 924                     if (dl - dp < 2)
 925                         return dp - dp0;
 926                     if (padding && sp != sl)
 927                         throw new IllegalArgumentException(
 928                             "Input buffer has wrong 4-byte ending unit");
 929                     da[dp++] = (byte)(bits >> 16);
 930                     da[dp++] = (byte)(bits >>  8);
 931                     mark = sp;
 932                 } else if (padding || shiftto != 18) {
 933                     throw new IllegalArgumentException(
 934                         "Last unit does not have enough valid bits");
 935                 }







 936                 return dp - dp0;
 937             } finally {
 938                 src.position(mark);
 939                 dst.position(dp);
 940             }
 941         }
 942 
 943         private int decodeBuffer(ByteBuffer src, ByteBuffer dst) {
 944             int[] base64 = isURL ? fromBase64URL : fromBase64;
 945             int   bits = 0;
 946             int   shiftto = 18;       // pos of first byte of 4-byte atom
 947             int    sp = src.position();
 948             int    sl = src.limit();
 949             int    dp = dst.position();
 950             int    dl = dst.limit();
 951             int    dp0 = dp;
 952             int    mark = sp;
 953             boolean padding = false;
 954 
 955             try {
 956                 while (sp < sl) {
 957                     int b = src.get(sp++) & 0xff;
 958                     if ((b = base64[b]) < 0) {
 959                         if (b == -2) {  // padding byte
 960                             padding = true;




 961                             break;
 962                         }
 963                         if (isMIME)     // skip if for rfc2045
 964                             continue;
 965                         else
 966                             throw new IllegalArgumentException(
 967                                 "Illegal base64 character " +
 968                                 Integer.toString(src.get(sp - 1), 16));
 969                     }
 970                     bits |= (b << shiftto);
 971                     shiftto -= 6;
 972                     if (shiftto < 0) {
 973                         if (dl < dp + 3)
 974                             return dp - dp0;
 975                         dst.put(dp++, (byte)(bits >> 16));
 976                         dst.put(dp++, (byte)(bits >>  8));
 977                         dst.put(dp++, (byte)(bits));
 978                         shiftto = 18;
 979                         bits = 0;
 980                         mark = sp;
 981                     }
 982                 }
 983                 if (shiftto == 6) {
 984                     if (dl - dp < 1)
 985                         return dp - dp0;
 986                     if (padding && (sp + 1 != sl || src.get(sp++) != '='))
 987                         throw new IllegalArgumentException(
 988                             "Input buffer has wrong 4-byte ending unit");
 989                      dst.put(dp++, (byte)(bits >> 16));
 990                      mark = sp;
 991                 } else if (shiftto == 0) {
 992                     if (dl - dp < 2)
 993                         return dp - dp0;
 994                     if (padding && sp != sl)
 995                         throw new IllegalArgumentException(
 996                             "Input buffer has wrong 4-byte ending unit");
 997                     dst.put(dp++, (byte)(bits >> 16));
 998                     dst.put(dp++, (byte)(bits >>  8));
 999                     mark = sp;
1000                 } else if (padding || shiftto != 18) {
1001                     throw new IllegalArgumentException(
1002                         "Last unit does not have enough valid bits");
1003                 }







1004                 return dp - dp0;
1005             } finally {
1006                 src.position(mark);
1007                 dst.position(dp);
1008             }
1009         }
1010 
1011         private int outLength(byte[] src, int sp, int sl) {
1012             int[] base64 = isURL ? fromBase64URL : fromBase64;
1013             int paddings = 0;
1014             int len = sl - sp;
1015             if (len == 0)
1016                 return 0;
1017             if (len < 2) {
1018                 if (isMIME && base64[0] == -1)
1019                     return 0;
1020                 throw new IllegalArgumentException(
1021                     "Input byte[] should at least have 2 bytes for base64 bytes");
1022             }
1023             if (src[sl - 1] == '=') {


1031                 int n = 0;
1032                 while (sp < sl) {
1033                     int b = src[sp++] & 0xff;
1034                     if (b == '=')
1035                         break;
1036                     if ((b = base64[b]) == -1)
1037                         n++;
1038                 }
1039                 len -= n;
1040             }
1041             if (paddings == 0 && (len & 0x3) !=  0)
1042                 paddings = 4 - (len & 0x3);
1043             return 3 * ((len + 3) / 4) - paddings;
1044         }
1045 
1046         private int decode0(byte[] src, int sp, int sl, byte[] dst) {
1047             int[] base64 = isURL ? fromBase64URL : fromBase64;
1048             int dp = 0;
1049             int bits = 0;
1050             int shiftto = 18;       // pos of first byte of 4-byte atom
1051             boolean padding = false;
1052             while (sp < sl) {
1053                 int b = src[sp++] & 0xff;
1054                 if ((b = base64[b]) < 0) {
1055                     if (b == -2) {     // padding byte
1056                         padding = true;









1057                         break;
1058                     }
1059                     if (isMIME)    // skip if for rfc2045
1060                         continue;
1061                     else
1062                         throw new IllegalArgumentException(
1063                             "Illegal base64 character " +
1064                             Integer.toString(src[sp - 1], 16));
1065                 }
1066                 bits |= (b << shiftto);
1067                 shiftto -= 6;
1068                 if (shiftto < 0) {
1069                     dst[dp++] = (byte)(bits >> 16);
1070                     dst[dp++] = (byte)(bits >>  8);
1071                     dst[dp++] = (byte)(bits);
1072                     shiftto = 18;
1073                     bits = 0;
1074                 }
1075             }
1076             // reach end of byte arry or hit padding '=' characters.
1077             // if '=' presents, they must be the last one or two.
1078             if (shiftto == 6) {           // xx==
1079                 if (padding && (sp + 1 != sl || src[sp] != '='))
1080                     throw new IllegalArgumentException(
1081                         "Input byte array has wrong 4-byte ending unit");
1082                 dst[dp++] = (byte)(bits >> 16);
1083             } else if (shiftto == 0) {    // xxx=
1084                 if (padding && sp != sl)
1085                     throw new IllegalArgumentException(
1086                         "Input byte array has wrong 4-byte ending unit");
1087                 dst[dp++] = (byte)(bits >> 16);
1088                 dst[dp++] = (byte)(bits >>  8);
1089             } else if (padding || shiftto != 18) {








1090                     throw new IllegalArgumentException(
1091                         "last unit does not have enough bytes");
1092             }
1093             return dp;
1094         }
1095     }
1096 
1097     /*
1098      * An output stream for encoding bytes into the Base64.
1099      */
1100     private static class EncOutputStream extends FilterOutputStream {
1101 
1102         private int leftover = 0;
1103         private int b0, b1, b2;
1104         private boolean closed = false;
1105 
1106         private final char[] base64;    // byte->base64 mapping
1107         private final byte[] newline;   // line separator, if needed
1108         private final int linemax;
1109         private int linepos = 0;
1110 
1111         EncOutputStream(OutputStream os,


1235                 throw new IOException("Stream is closed");
1236             if (eof && nextout < 0)    // eof and no leftover
1237                 return -1;
1238             if (off < 0 || len < 0 || len > b.length - off)
1239                 throw new IndexOutOfBoundsException();
1240             int oldOff = off;
1241             if (nextout >= 0) {       // leftover output byte(s) in bits buf
1242                 do {
1243                     if (len == 0)
1244                         return off - oldOff;
1245                     b[off++] = (byte)(bits >> nextout);
1246                     len--;
1247                     nextout -= 8;
1248                 } while (nextout >= 0);
1249                 bits = 0;
1250             }
1251             while (len > 0) {
1252                 int v = is.read();
1253                 if (v == -1) {
1254                     eof = true;
1255                     if (nextin != 18)
1256                         throw new IOException("Base64 stream has un-decoded dangling byte(s).");














1257                     if (off == oldOff)
1258                         return -1;
1259                     else
1260                         return off - oldOff;
1261                 }
1262                 if (v == '=') {                  // padding byte(s)
1263                     if (nextin != 6 && nextin != 0) {
1264                         throw new IOException("Illegal base64 ending sequence:" + nextin);
1265                     }
1266                     b[off++] = (byte)(bits >> (16));
1267                     len--;
1268                     if (nextin == 0) {           // only one padding byte
1269                         if (len == 0) {          // no enough output space
1270                             bits >>= 8;          // shift to lowest byte
1271                             nextout = 0;
1272                         } else {
1273                             b[off++] = (byte) (bits >>  8);
1274                         }
1275                     }
1276                     eof = true;




  47  * <p> Uses "The Base64 Alphabet" as specified in Table 1 of
  48  *     RFC 4648 and RFC 2045 for encoding and decoding operation.
  49  *     The encoder does not add any line feed (line separator)
  50  *     character. The decoder rejects data that contains characters
  51  *     outside the base64 alphabet.</p></li>
  52  *
  53  * <a name="url">
  54  * <li><b>URL and Filename safe</b>
  55  * <p> Uses the "URL and Filename safe Base64 Alphabet" as specified
  56  *     in Table 2 of RFC 4648 for encoding and decoding. The
  57  *     encoder does not add any line feed (line separator) character.
  58  *     The decoder rejects data that contains characters outside the
  59  *     base64 alphabet.</p></li>
  60  *
  61  * <a name="mime">
  62  * <li><b>MIME</b>
  63  * <p> Uses the "The Base64 Alphabet" as specified in Table 1 of
  64  *     RFC 2045 for encoding and decoding operation. The encoded output
  65  *     must be represented in lines of no more than 76 characters each
  66  *     and uses a carriage return {@code '\r'} followed immediately by
  67  *     a linefeed {@code '\n'} as the line separator. No line separator
  68  *     is added to the end of the encoded output. All line separators
  69  *     or other characters not found in the base64 alphabet table are
  70  *     ignored in decoding operation.</p></li>
  71  * </ul>
  72  *
  73  * <p> Unless otherwise noted, passing a {@code null} argument to a
  74  * method of this class will cause a {@link java.lang.NullPointerException
  75  * NullPointerException} to be thrown.
  76  *
  77  * @author  Xueming Shen
  78  * @since   1.8
  79  */
  80 
  81 public class Base64 {
  82 
  83     private Base64() {}
  84 
  85     /**
  86      * Returns a {@link Encoder} that encodes using the
  87      * <a href="#basic">Basic</a> type base64 encoding scheme.
  88      *


 598                 dst[dp++] = (byte)base64[b0 >> 2];
 599                 if (sp == end) {
 600                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
 601                     dst[dp++] = '=';
 602                     dst[dp++] = '=';
 603                 } else {
 604                     int b1 = src[sp++] & 0xff;
 605                     dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
 606                     dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
 607                     dst[dp++] = '=';
 608                 }
 609             }
 610             return dp;
 611         }
 612     }
 613 
 614     /**
 615      * This class implements a decoder for decoding byte data using the
 616      * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
 617      *
 618      * <p> The Base64 padding character {@code '='} is accepted and
 619      * interpreted as the end of the encoded byte data, but is not
 620      * required. So if the final unit of the encoded byte data only has
 621      * two or three Base64 characters (without the corresponding padding
 622      * character(s) padded), they are decoded as if followed by padding
 623      * character(s).
 624      *
 625      * <p> Instances of {@link Decoder} class are safe for use by
 626      * multiple concurrent threads.
 627      *
 628      * <p> Unless otherwise noted, passing a {@code null} argument to
 629      * a method of this class will cause a
 630      * {@link java.lang.NullPointerException NullPointerException} to
 631      * be thrown.
 632      *
 633      * @see     Encoder
 634      * @since   1.8
 635      */
 636     public static class Decoder {
 637 
 638         private final boolean isURL;
 639         private final boolean isMIME;
 640 
 641         private Decoder(boolean isURL, boolean isMIME) {
 642             this.isURL = isURL;
 643             this.isMIME = isMIME;
 644         }


 848          * @throws  IllegalArgumentException
 849          *          if {@code src} is not in valid Base64 scheme.
 850          */
 851         public int decode(ByteBuffer src, ByteBuffer dst) {
 852             int sp0 = src.position();
 853             int dp0 = dst.position();
 854             try {
 855                 if (src.hasArray() && dst.hasArray())
 856                     return decodeArray(src, dst);
 857                 return decodeBuffer(src, dst);
 858             } catch (IllegalArgumentException iae) {
 859                 src.position(sp0);
 860                 dst.position(dp0);
 861                 throw iae;
 862             }
 863         }
 864 
 865         /**
 866          * Returns an input stream for decoding {@link Base64} encoded byte stream.
 867          *
 868          * <p> The {@code read}  methods of the returned {@code InputStream} will
 869          * throw {@code IOException} when reading bytes that cannot be decoded.
 870          *
 871          * <p> Closing the returned input stream will close the underlying
 872          * input stream.
 873          *
 874          * @param   is
 875          *          the input stream
 876          *
 877          * @return  the input stream for decoding the specified Base64 encoded
 878          *          byte stream
 879          */
 880         public InputStream wrap(InputStream is) {
 881             Objects.requireNonNull(is);
 882             return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
 883         }
 884 
 885         private int decodeArray(ByteBuffer src, ByteBuffer dst) {
 886             int[] base64 = isURL ? fromBase64URL : fromBase64;
 887             int   bits = 0;
 888             int   shiftto = 18;       // pos of first byte of 4-byte atom
 889             byte[] sa = src.array();
 890             int    sp = src.arrayOffset() + src.position();
 891             int    sl = src.arrayOffset() + src.limit();
 892             byte[] da = dst.array();
 893             int    dp = dst.arrayOffset() + dst.position();
 894             int    dl = dst.arrayOffset() + dst.limit();
 895             int    dp0 = dp;
 896             int    mark = sp;

 897             try {
 898                 while (sp < sl) {
 899                     int b = sa[sp++] & 0xff;
 900                     if ((b = base64[b]) < 0) {
 901                         if (b == -2) {   // padding byte
 902                             if (shiftto == 6 && (sp == sl || sa[sp++] != '=') ||
 903                                 shiftto == 18) {
 904                                 throw new IllegalArgumentException(
 905                                      "Input byte array has wrong 4-byte ending unit");
 906                             }
 907                             break;
 908                         }
 909                         if (isMIME)     // skip if for rfc2045
 910                             continue;
 911                         else
 912                             throw new IllegalArgumentException(
 913                                 "Illegal base64 character " +
 914                                 Integer.toString(sa[sp - 1], 16));
 915                     }
 916                     bits |= (b << shiftto);
 917                     shiftto -= 6;
 918                     if (shiftto < 0) {
 919                         if (dl < dp + 3)
 920                             return dp - dp0;
 921                         da[dp++] = (byte)(bits >> 16);
 922                         da[dp++] = (byte)(bits >>  8);
 923                         da[dp++] = (byte)(bits);
 924                         shiftto = 18;
 925                         bits = 0;
 926                         mark = sp;
 927                     }
 928                 }
 929                 if (shiftto == 6) {
 930                     if (dl - dp < 1)
 931                         return dp - dp0;



 932                     da[dp++] = (byte)(bits >> 16);

 933                 } else if (shiftto == 0) {
 934                     if (dl - dp < 2)
 935                         return dp - dp0;



 936                     da[dp++] = (byte)(bits >> 16);
 937                     da[dp++] = (byte)(bits >>  8);
 938                 } else if (shiftto == 12) {

 939                     throw new IllegalArgumentException(
 940                         "Last unit does not have enough valid bits");
 941                 }
 942                 while (sp < sl) {
 943                     if (isMIME && base64[sa[sp++]] < 0)
 944                         continue;
 945                     throw new IllegalArgumentException(
 946                         "Input byte array has incorrect ending byte at " + sp);
 947                 }
 948                 mark = sp;
 949                 return dp - dp0;
 950             } finally {
 951                 src.position(mark);
 952                 dst.position(dp);
 953             }
 954         }
 955 
 956         private int decodeBuffer(ByteBuffer src, ByteBuffer dst) {
 957             int[] base64 = isURL ? fromBase64URL : fromBase64;
 958             int   bits = 0;
 959             int   shiftto = 18;       // pos of first byte of 4-byte atom
 960             int    sp = src.position();
 961             int    sl = src.limit();
 962             int    dp = dst.position();
 963             int    dl = dst.limit();
 964             int    dp0 = dp;
 965             int    mark = sp;


 966             try {
 967                 while (sp < sl) {
 968                     int b = src.get(sp++) & 0xff;
 969                     if ((b = base64[b]) < 0) {
 970                         if (b == -2) {  // padding byte
 971                             if (shiftto == 6 && (sp == sl || src.get(sp++) != '=') ||
 972                                 shiftto == 18) {
 973                                 throw new IllegalArgumentException(
 974                                      "Input byte array has wrong 4-byte ending unit");
 975                             }
 976                             break;
 977                         }
 978                         if (isMIME)     // skip if for rfc2045
 979                             continue;
 980                         else
 981                             throw new IllegalArgumentException(
 982                                 "Illegal base64 character " +
 983                                 Integer.toString(src.get(sp - 1), 16));
 984                     }
 985                     bits |= (b << shiftto);
 986                     shiftto -= 6;
 987                     if (shiftto < 0) {
 988                         if (dl < dp + 3)
 989                             return dp - dp0;
 990                         dst.put(dp++, (byte)(bits >> 16));
 991                         dst.put(dp++, (byte)(bits >>  8));
 992                         dst.put(dp++, (byte)(bits));
 993                         shiftto = 18;
 994                         bits = 0;
 995                         mark = sp;
 996                     }
 997                 }
 998                 if (shiftto == 6) {
 999                     if (dl - dp < 1)
1000                         return dp - dp0;



1001                      dst.put(dp++, (byte)(bits >> 16));

1002                 } else if (shiftto == 0) {
1003                     if (dl - dp < 2)
1004                         return dp - dp0;



1005                     dst.put(dp++, (byte)(bits >> 16));
1006                     dst.put(dp++, (byte)(bits >>  8));
1007                 } else if (shiftto == 12) {

1008                     throw new IllegalArgumentException(
1009                         "Last unit does not have enough valid bits");
1010                 }
1011                 while (sp < sl) {
1012                     if (isMIME && base64[src.get(sp++)] < 0)
1013                         continue;
1014                     throw new IllegalArgumentException(
1015                         "Input byte array has incorrect ending byte at " + sp);
1016                 }
1017                 mark = sp;
1018                 return dp - dp0;
1019             } finally {
1020                 src.position(mark);
1021                 dst.position(dp);
1022             }
1023         }
1024 
1025         private int outLength(byte[] src, int sp, int sl) {
1026             int[] base64 = isURL ? fromBase64URL : fromBase64;
1027             int paddings = 0;
1028             int len = sl - sp;
1029             if (len == 0)
1030                 return 0;
1031             if (len < 2) {
1032                 if (isMIME && base64[0] == -1)
1033                     return 0;
1034                 throw new IllegalArgumentException(
1035                     "Input byte[] should at least have 2 bytes for base64 bytes");
1036             }
1037             if (src[sl - 1] == '=') {


1045                 int n = 0;
1046                 while (sp < sl) {
1047                     int b = src[sp++] & 0xff;
1048                     if (b == '=')
1049                         break;
1050                     if ((b = base64[b]) == -1)
1051                         n++;
1052                 }
1053                 len -= n;
1054             }
1055             if (paddings == 0 && (len & 0x3) !=  0)
1056                 paddings = 4 - (len & 0x3);
1057             return 3 * ((len + 3) / 4) - paddings;
1058         }
1059 
1060         private int decode0(byte[] src, int sp, int sl, byte[] dst) {
1061             int[] base64 = isURL ? fromBase64URL : fromBase64;
1062             int dp = 0;
1063             int bits = 0;
1064             int shiftto = 18;       // pos of first byte of 4-byte atom

1065             while (sp < sl) {
1066                 int b = src[sp++] & 0xff;
1067                 if ((b = base64[b]) < 0) {
1068                     if (b == -2) {     // padding byte '='
1069                         // xx=   shiftto==6&&sp==sl missing last =
1070                         // xx=y  shiftto==6 last is not =
1071                         // =     shiftto==18 unnecessary padding
1072                         // x=    shiftto==12 be taken care later
1073                         //       together with single x, invalid anyway
1074                         if (shiftto == 6 && (sp == sl || src[sp++] != '=') ||
1075                             shiftto == 18) {
1076                             throw new IllegalArgumentException(
1077                                 "Input byte array has wrong 4-byte ending unit");
1078                         }
1079                         break;
1080                     }
1081                     if (isMIME)    // skip if for rfc2045
1082                         continue;
1083                     else
1084                         throw new IllegalArgumentException(
1085                             "Illegal base64 character " +
1086                             Integer.toString(src[sp - 1], 16));
1087                 }
1088                 bits |= (b << shiftto);
1089                 shiftto -= 6;
1090                 if (shiftto < 0) {
1091                     dst[dp++] = (byte)(bits >> 16);
1092                     dst[dp++] = (byte)(bits >>  8);
1093                     dst[dp++] = (byte)(bits);
1094                     shiftto = 18;
1095                     bits = 0;
1096                 }
1097             }
1098             // reached end of byte array or hit padding '=' characters.
1099             if (shiftto == 6) {




1100                 dst[dp++] = (byte)(bits >> 16);
1101             } else if (shiftto == 0) {



1102                 dst[dp++] = (byte)(bits >> 16);
1103                 dst[dp++] = (byte)(bits >>  8);
1104             } else if (shiftto == 12) {
1105                 throw new IllegalArgumentException(
1106                     "Last unit does not have enough valid bits");
1107             }
1108             // anything left is invalid, if is not MIME.
1109             // if MIME, ignore all non-base64 character
1110             while (sp < sl) {
1111                 if (isMIME && base64[src[sp++]] < 0)
1112                     continue;
1113                 throw new IllegalArgumentException(
1114                     "Input byte array has incorrect ending byte at " + sp);
1115             }
1116             return dp;
1117         }
1118     }
1119 
1120     /*
1121      * An output stream for encoding bytes into the Base64.
1122      */
1123     private static class EncOutputStream extends FilterOutputStream {
1124 
1125         private int leftover = 0;
1126         private int b0, b1, b2;
1127         private boolean closed = false;
1128 
1129         private final char[] base64;    // byte->base64 mapping
1130         private final byte[] newline;   // line separator, if needed
1131         private final int linemax;
1132         private int linepos = 0;
1133 
1134         EncOutputStream(OutputStream os,


1258                 throw new IOException("Stream is closed");
1259             if (eof && nextout < 0)    // eof and no leftover
1260                 return -1;
1261             if (off < 0 || len < 0 || len > b.length - off)
1262                 throw new IndexOutOfBoundsException();
1263             int oldOff = off;
1264             if (nextout >= 0) {       // leftover output byte(s) in bits buf
1265                 do {
1266                     if (len == 0)
1267                         return off - oldOff;
1268                     b[off++] = (byte)(bits >> nextout);
1269                     len--;
1270                     nextout -= 8;
1271                 } while (nextout >= 0);
1272                 bits = 0;
1273             }
1274             while (len > 0) {
1275                 int v = is.read();
1276                 if (v == -1) {
1277                     eof = true;
1278                     if (nextin != 18) {
1279                         if (nextin == 12)
1280                             throw new IOException("Base64 stream has one un-decoded dangling byte.");
1281                         // treat ending xx/xxx without padding character legal.
1282                         // same logic as v == 'v' below
1283                         b[off++] = (byte)(bits >> (16));
1284                         len--;
1285                         if (nextin == 0) {           // only one padding byte
1286                             if (len == 0) {          // no enough output space
1287                                 bits >>= 8;          // shift to lowest byte
1288                                 nextout = 0;
1289                             } else {
1290                                 b[off++] = (byte) (bits >>  8);
1291                             }
1292                         }
1293                     }
1294                     if (off == oldOff)
1295                         return -1;
1296                     else
1297                         return off - oldOff;
1298                 }
1299                 if (v == '=') {                  // padding byte(s)
1300                     if (nextin != 6 && nextin != 0) {
1301                         throw new IOException("Illegal base64 ending sequence:" + nextin);
1302                     }
1303                     b[off++] = (byte)(bits >> (16));
1304                     len--;
1305                     if (nextin == 0) {           // only one padding byte
1306                         if (len == 0) {          // no enough output space
1307                             bits >>= 8;          // shift to lowest byte
1308                             nextout = 0;
1309                         } else {
1310                             b[off++] = (byte) (bits >>  8);
1311                         }
1312                     }
1313                     eof = true;