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;
|