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
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);
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() {
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
196 private Encoder(boolean isURL, byte[] newline, int linemax) {
197 this.isURL = isURL;
198 this.newline = newline;
199 this.linemax = linemax;
200 }
201
202 /**
203 * This array is a lookup table that translates 6-bit positive integer
204 * index values into their "Base64 Alphabet" equivalents as specified
205 * in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
206 */
207 private static final char[] toBase64 = {
208 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
209 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
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 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
213 };
214
215 /**
216 * It's the lookup table for "URL and Filename safe Base64" as specified
217 * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
218 * '_'. This table is used when BASE64_URL is specified.
219 */
220 private static final char[] toBase64URL = {
221 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
222 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
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 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
226 };
227
228 private static final int MIMELINEMAX = 76;
229 private static final byte[] CRLF = new byte[] {'\r', '\n'};
230
231 static final Encoder RFC4648 = new Encoder(false, null, -1);
232 static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1);
233 static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX);
234
235 /**
236 * Encodes all bytes from the specified byte array into a newly-allocated
237 * byte array using the {@link Base64} encoding scheme. The returned byte
238 * array is of the length of the resulting bytes.
239 *
240 * @param src
241 * the byte array to encode
242 * @return A newly-allocated byte array containing the resulting
243 * encoded bytes.
244 */
245 public byte[] encode(byte[] src) {
246 int len = 4 * ((src.length + 2) / 3); // dst array size
247 if (linemax > 0) // line separators
248 len += (len - 1) / linemax * newline.length;
249 byte[] dst = new byte[len];
250 int ret = encode0(src, 0, src.length, dst);
251 if (ret != dst.length)
252 return Arrays.copyOf(dst, ret);
253 return dst;
254 }
255
256 /**
257 * Encodes all bytes from the specified byte array using the
258 * {@link Base64} encoding scheme, writing the resulting bytes to the
259 * given output byte array, starting at offset 0.
260 *
261 * <p> It is the responsibility of the invoker of this method to make
262 * sure the output byte array {@code dst} has enough space for encoding
263 * all bytes from the input byte array. No bytes will be written to the
264 * output byte array if the output byte array is not big enough.
265 *
266 * @param src
267 * the byte array to encode
268 * @param dst
269 * the output byte array
270 * @return The number of bytes written to the output byte array
271 *
272 * @throws IllegalArgumentException if {@code dst} does not have enough
273 * space for encoding all input bytes.
274 */
275 public int encode(byte[] src, byte[] dst) {
276 int len = 4 * ((src.length + 2) / 3); // dst array size
277 if (linemax > 0) {
278 len += (len - 1) / linemax * newline.length;
279 }
280 if (dst.length < len)
281 throw new IllegalArgumentException(
282 "Output byte array is too small for encoding all input bytes");
283 return encode0(src, 0, src.length, dst);
284 }
285
286 /**
287 * Encodes the specified byte array into a String using the {@link Base64}
288 * encoding scheme.
289 *
290 * <p> This method first encodes all input bytes into a base64 encoded
291 * byte array and then constructs a new String by using the encoded byte
292 * array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1
293 * ISO-8859-1} charset.
294 *
295 * <p> In other words, an invocation of this method has exactly the same
296 * effect as invoking
297 * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.
298 *
299 * @param src
304 public String encodeToString(byte[] src) {
305 byte[] encoded = encode(src);
306 return new String(encoded, 0, 0, encoded.length);
307 }
308
309 /**
310 * Encodes all remaining bytes from the specified byte buffer into
311 * a newly-allocated ByteBuffer using the {@link Base64} encoding
312 * scheme.
313 *
314 * Upon return, the source buffer's position will be updated to
315 * its limit; its limit will not have been changed. The returned
316 * output buffer's position will be zero and its limit will be the
317 * number of resulting encoded bytes.
318 *
319 * @param buffer
320 * the source ByteBuffer to encode
321 * @return A newly-allocated byte buffer containing the encoded bytes.
322 */
323 public ByteBuffer encode(ByteBuffer buffer) {
324 int len = 4 * ((buffer.remaining() + 2) / 3);
325 if (linemax > 0)
326 len += (len - 1) / linemax * newline.length;
327 byte[] dst = new byte[len];
328 int ret = 0;
329 if (buffer.hasArray()) {
330 ret = encode0(buffer.array(),
331 buffer.arrayOffset() + buffer.position(),
332 buffer.arrayOffset() + buffer.limit(),
333 dst);
334 buffer.position(buffer.limit());
335 } else {
336 byte[] src = new byte[buffer.remaining()];
337 buffer.get(src);
338 ret = encode0(src, 0, src.length, dst);
339 }
340 if (ret != dst.length)
341 dst = Arrays.copyOf(dst, ret);
342 return ByteBuffer.wrap(dst);
343 }
344
345 /**
346 * Encodes as many bytes as possible from the input byte buffer
398 return encodeBuffer(src, dst, bytesOut);
399 }
400
401 /**
402 * Wraps an output stream for encoding byte data using the {@link Base64}
403 * encoding scheme.
404 *
405 * <p> It is recommended to promptly close the returned output stream after
406 * use, during which it will flush all possible leftover bytes to the underlying
407 * output stream. Closing the returned output stream will close the underlying
408 * output stream.
409 *
410 * @param os
411 * the output stream.
412 * @return the output stream for encoding the byte data into the
413 * specified Base64 encoded format
414 */
415 public OutputStream wrap(OutputStream os) {
416 Objects.requireNonNull(os);
417 return new EncOutputStream(os, isURL ? toBase64URL : toBase64,
418 newline, linemax);
419 }
420
421 private int encodeArray(ByteBuffer src, ByteBuffer dst, int bytesOut) {
422 char[] base64 = isURL? toBase64URL : toBase64;
423 byte[] sa = src.array();
424 int sp = src.arrayOffset() + src.position();
425 int sl = src.arrayOffset() + src.limit();
426 byte[] da = dst.array();
427 int dp = dst.arrayOffset() + dst.position();
428 int dl = dst.arrayOffset() + dst.limit();
429 int dp00 = dp;
430 int dpos = 0; // dp of each line
431 if (linemax > 0 && bytesOut > 0)
432 dpos = bytesOut % (linemax + newline.length);
433 try {
434 if (dpos == linemax && sp < src.limit()) {
435 if (dp + newline.length > dl)
436 return dp - dp00 + bytesOut;
437 for (byte b : newline){
438 dst.put(dp++, b);
459 }
460 int n = (sl0 - sp) / 3 * 4;
461 dpos += n;
462 dp += n;
463 sp = sl0;
464 if (dpos == linemax && sp < src.limit()) {
465 if (dp + newline.length > dl)
466 return dp - dp00 + bytesOut;
467 for (byte b : newline){
468 da[dp++] = b;
469 }
470 dpos = 0;
471 }
472 }
473 sl = src.arrayOffset() + src.limit();
474 if (sp < sl && dl >= dp + 4) { // 1 or 2 leftover bytes
475 int b0 = sa[sp++] & 0xff;
476 da[dp++] = (byte)base64[b0 >> 2];
477 if (sp == sl) {
478 da[dp++] = (byte)base64[(b0 << 4) & 0x3f];
479 da[dp++] = '=';
480 da[dp++] = '=';
481 } else {
482 int b1 = sa[sp++] & 0xff;
483 da[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
484 da[dp++] = (byte)base64[(b1 << 2) & 0x3f];
485 da[dp++] = '=';
486 }
487 }
488 return dp - dp00 + bytesOut;
489 } finally {
490 src.position(sp - src.arrayOffset());
491 dst.position(dp - dst.arrayOffset());
492 }
493 }
494
495 private int encodeBuffer(ByteBuffer src, ByteBuffer dst, int bytesOut) {
496 char[] base64 = isURL? toBase64URL : toBase64;
497 int sp = src.position();
498 int sl = src.limit();
499 int dp = dst.position();
500 int dl = dst.limit();
501 int dp00 = dp;
502
503 int dpos = 0; // dp of each line
504 if (linemax > 0 && bytesOut > 0)
505 dpos = bytesOut % (linemax + newline.length);
506 try {
507 if (dpos == linemax && sp < src.limit()) {
531 dst.put(dp0++, (byte)base64[bits & 0x3f]);
532 }
533 int n = (sl0 - sp) / 3 * 4;
534 dpos += n;
535 dp += n;
536 sp = sl0;
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 }
546 if (sp < src.limit() && dl >= dp + 4) { // 1 or 2 leftover bytes
547 int b0 = src.get(sp++) & 0xff;
548 dst.put(dp++, (byte)base64[b0 >> 2]);
549 if (sp == src.limit()) {
550 dst.put(dp++, (byte)base64[(b0 << 4) & 0x3f]);
551 dst.put(dp++, (byte)'=');
552 dst.put(dp++, (byte)'=');
553 } else {
554 int b1 = src.get(sp++) & 0xff;
555 dst.put(dp++, (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
556 dst.put(dp++, (byte)base64[(b1 << 2) & 0x3f]);
557 dst.put(dp++, (byte)'=');
558 }
559 }
560 return dp - dp00 + bytesOut;
561 } finally {
562 src.position(sp);
563 dst.position(dp);
564 }
565 }
566
567 private int encode0(byte[] src, int off, int end, byte[] dst) {
568 char[] base64 = isURL ? toBase64URL : toBase64;
569 int sp = off;
570 int slen = (end - off) / 3 * 3;
571 int sl = off + slen;
572 if (linemax > 0 && slen > linemax / 4 * 3)
573 slen = linemax / 4 * 3;
574 int dp = 0;
575 while (sp < sl) {
576 int sl0 = Math.min(sp + slen, sl);
577 for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) {
578 int bits = (src[sp0++] & 0xff) << 16 |
579 (src[sp0++] & 0xff) << 8 |
580 (src[sp0++] & 0xff);
581 dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
582 dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
583 dst[dp0++] = (byte)base64[(bits >>> 6) & 0x3f];
584 dst[dp0++] = (byte)base64[bits & 0x3f];
585 }
586 int dlen = (sl0 - sp) / 3 * 4;
587 dp += dlen;
588 sp = sl0;
589 if (dlen == linemax && sp < end) {
590 for (byte b : newline){
591 dst[dp++] = b;
592 }
593 }
594 }
595 if (sp < end) { // 1 or 2 leftover bytes
596 int b0 = src[sp++] & 0xff;
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> The Base64 padding character {@code '='} is accepted and
618 * interpreted as the end of the encoded byte data, but is not
619 * required. So if the final unit of the encoded byte data only has
620 * two or three Base64 characters (without the corresponding padding
621 * character(s) padded), they are decoded as if followed by padding
622 * character(s).
623 * <p>
624 * For decoders that use the <a href="#basic">Basic</a> and
625 * <a href="#url">URL and Filename safe</a> type base64 scheme, and
626 * if there is padding character present in the final unit, the
627 * correct number of padding character(s) must be present, otherwise
628 * {@code IllegalArgumentException} ({@code IOException} when reading
1132 if (sp < sl && !isMIME) {
1133 throw new IllegalArgumentException(
1134 "Input byte array has incorrect ending byte at " + sp);
1135 }
1136 return dp;
1137 }
1138 }
1139
1140 /*
1141 * An output stream for encoding bytes into the Base64.
1142 */
1143 private static class EncOutputStream extends FilterOutputStream {
1144
1145 private int leftover = 0;
1146 private int b0, b1, b2;
1147 private boolean closed = false;
1148
1149 private final char[] base64; // byte->base64 mapping
1150 private final byte[] newline; // line separator, if needed
1151 private final int linemax;
1152 private int linepos = 0;
1153
1154 EncOutputStream(OutputStream os,
1155 char[] base64, byte[] newline, int linemax) {
1156 super(os);
1157 this.base64 = base64;
1158 this.newline = newline;
1159 this.linemax = linemax;
1160 }
1161
1162 @Override
1163 public void write(int b) throws IOException {
1164 byte[] buf = new byte[1];
1165 buf[0] = (byte)(b & 0xff);
1166 write(buf, 0, 1);
1167 }
1168
1169 private void checkNewline() throws IOException {
1170 if (linepos == linemax) {
1171 out.write(newline);
1172 linepos = 0;
1173 }
1174 }
1175
1176 @Override
1177 public void write(byte[] b, int off, int len) throws IOException {
1178 if (closed)
1179 throw new IOException("Stream is closed");
1211 out.write(base64[(bits >>> 6) & 0x3f]);
1212 out.write(base64[bits & 0x3f]);
1213 linepos += 4;
1214 }
1215 if (leftover == 1) {
1216 b0 = b[off++] & 0xff;
1217 } else if (leftover == 2) {
1218 b0 = b[off++] & 0xff;
1219 b1 = b[off++] & 0xff;
1220 }
1221 }
1222
1223 @Override
1224 public void close() throws IOException {
1225 if (!closed) {
1226 closed = true;
1227 if (leftover == 1) {
1228 checkNewline();
1229 out.write(base64[b0 >> 2]);
1230 out.write(base64[(b0 << 4) & 0x3f]);
1231 out.write('=');
1232 out.write('=');
1233 } else if (leftover == 2) {
1234 checkNewline();
1235 out.write(base64[b0 >> 2]);
1236 out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
1237 out.write(base64[(b1 << 2) & 0x3f]);
1238 out.write('=');
1239 }
1240 leftover = 0;
1241 out.close();
1242 }
1243 }
1244 }
1245
1246 /*
1247 * An input stream for decoding Base64 bytes
1248 */
1249 private static class DecInputStream extends InputStream {
1250
1251 private final InputStream is;
1252 private final boolean isMIME;
1253 private final int[] base64; // base64 -> byte mapping
1254 private int bits = 0; // 24-bit buffer for decoding
1255 private int nextin = 18; // next available "off" in "bits" for input;
1256 // -> 18, 12, 6, 0
1257 private int nextout = -8; // next available "off" in "bits" for output;
1258 // -> 8, 0, -8 (no byte for output)
1259 private boolean eof = false;
|
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
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() {
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
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
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);
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()) {
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
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");
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;
|