1 /*
2 * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util;
27
28 import java.io.FilterOutputStream;
29 import java.io.InputStream;
30 import java.io.IOException;
31 import java.io.OutputStream;
32 import java.nio.ByteBuffer;
33 import java.nio.charset.StandardCharsets;
34 import jdk.internal.HotSpotIntrinsicCandidate;
35
36 /**
37 * This class consists exclusively of static methods for obtaining
38 * encoders and decoders for the Base64 encoding scheme. The
39 * implementation of this class supports the following types of Base64
40 * as specified in
41 * <a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a> and
42 * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>.
43 *
44 * <ul>
45 * <li><a id="basic"><b>Basic</b></a>
46 * <p> Uses "The Base64 Alphabet" as specified in Table 1 of
47 * RFC 4648 and RFC 2045 for encoding and decoding operation.
48 * The encoder does not add any line feed (line separator)
49 * character. The decoder rejects data that contains characters
50 * outside the base64 alphabet.</p></li>
51 *
52 * <li><a id="url"><b>URL and Filename safe</b></a>
169 * Returns a {@link Decoder} that decodes using the
170 * <a href="#mime">MIME</a> type base64 decoding scheme.
171 *
172 * @return A Base64 decoder.
173 */
174 public static Decoder getMimeDecoder() {
175 return Decoder.RFC2045;
176 }
177
178 /**
179 * This class implements an encoder for encoding byte data using
180 * the Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
181 *
182 * <p> Instances of {@link Encoder} class are safe for use by
183 * multiple concurrent threads.
184 *
185 * <p> Unless otherwise noted, passing a {@code null} argument to
186 * a method of this class will cause a
187 * {@link java.lang.NullPointerException NullPointerException} to
188 * be thrown.
189 *
190 * @see Decoder
191 * @since 1.8
192 */
193 public static class Encoder {
194
195 private final byte[] newline;
196 private final int linemax;
197 private final boolean isURL;
198 private final boolean doPadding;
199
200 private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) {
201 this.isURL = isURL;
202 this.newline = newline;
203 this.linemax = linemax;
204 this.doPadding = doPadding;
205 }
206
207 /**
208 * This array is a lookup table that translates 6-bit positive integer
220 /**
221 * It's the lookup table for "URL and Filename safe Base64" as specified
222 * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
223 * '_'. This table is used when BASE64_URL is specified.
224 */
225 private static final char[] toBase64URL = {
226 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
227 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
228 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
229 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
230 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
231 };
232
233 private static final int MIMELINEMAX = 76;
234 private static final byte[] CRLF = new byte[] {'\r', '\n'};
235
236 static final Encoder RFC4648 = new Encoder(false, null, -1, true);
237 static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
238 static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
239
240 private final int outLength(int srclen) {
241 int len = 0;
242 if (doPadding) {
243 len = 4 * ((srclen + 2) / 3);
244 } else {
245 int n = srclen % 3;
246 len = 4 * (srclen / 3) + (n == 0 ? 0 : n + 1);
247 }
248 if (linemax > 0) // line separators
249 len += (len - 1) / linemax * newline.length;
250 return len;
251 }
252
253 /**
254 * Encodes all bytes from the specified byte array into a newly-allocated
255 * byte array using the {@link Base64} encoding scheme. The returned byte
256 * array is of the length of the resulting bytes.
257 *
258 * @param src
259 * the byte array to encode
260 * @return A newly-allocated byte array containing the resulting
261 * encoded bytes.
262 */
263 public byte[] encode(byte[] src) {
264 int len = outLength(src.length); // dst array size
265 byte[] dst = new byte[len];
266 int ret = encode0(src, 0, src.length, dst);
267 if (ret != dst.length)
268 return Arrays.copyOf(dst, ret);
269 return dst;
270 }
271
272 /**
273 * Encodes all bytes from the specified byte array using the
274 * {@link Base64} encoding scheme, writing the resulting bytes to the
275 * given output byte array, starting at offset 0.
276 *
277 * <p> It is the responsibility of the invoker of this method to make
278 * sure the output byte array {@code dst} has enough space for encoding
279 * all bytes from the input byte array. No bytes will be written to the
280 * output byte array if the output byte array is not big enough.
281 *
282 * @param src
283 * the byte array to encode
284 * @param dst
285 * the output byte array
286 * @return The number of bytes written to the output byte array
287 *
288 * @throws IllegalArgumentException if {@code dst} does not have enough
289 * space for encoding all input bytes.
290 */
291 public int encode(byte[] src, byte[] dst) {
292 int len = outLength(src.length); // dst array size
293 if (dst.length < len)
294 throw new IllegalArgumentException(
295 "Output byte array is too small for encoding all input bytes");
296 return encode0(src, 0, src.length, dst);
297 }
298
299 /**
300 * Encodes the specified byte array into a String using the {@link Base64}
301 * encoding scheme.
302 *
303 * <p> This method first encodes all input bytes into a base64 encoded
304 * byte array and then constructs a new String by using the encoded byte
305 * array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1
306 * ISO-8859-1} charset.
307 *
308 * <p> In other words, an invocation of this method has exactly the same
309 * effect as invoking
310 * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.
311 *
312 * @param src
313 * the byte array to encode
314 * @return A String containing the resulting Base64 encoded characters
315 */
316 @SuppressWarnings("deprecation")
317 public String encodeToString(byte[] src) {
318 byte[] encoded = encode(src);
319 return new String(encoded, 0, 0, encoded.length);
320 }
321
322 /**
323 * Encodes all remaining bytes from the specified byte buffer into
324 * a newly-allocated ByteBuffer using the {@link Base64} encoding
325 * scheme.
326 *
327 * Upon return, the source buffer's position will be updated to
328 * its limit; its limit will not have been changed. The returned
329 * output buffer's position will be zero and its limit will be the
330 * number of resulting encoded bytes.
331 *
332 * @param buffer
333 * the source ByteBuffer to encode
334 * @return A newly-allocated byte buffer containing the encoded bytes.
335 */
336 public ByteBuffer encode(ByteBuffer buffer) {
337 int len = outLength(buffer.remaining());
338 byte[] dst = new byte[len];
339 int ret = 0;
340 if (buffer.hasArray()) {
341 ret = encode0(buffer.array(),
342 buffer.arrayOffset() + buffer.position(),
343 buffer.arrayOffset() + buffer.limit(),
344 dst);
345 buffer.position(buffer.limit());
346 } else {
347 byte[] src = new byte[buffer.remaining()];
348 buffer.get(src);
349 ret = encode0(src, 0, src.length, dst);
350 }
351 if (ret != dst.length)
352 dst = Arrays.copyOf(dst, ret);
353 return ByteBuffer.wrap(dst);
354 }
355
356 /**
357 * Wraps an output stream for encoding byte data using the {@link Base64}
452 * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
453 *
454 * <p> The Base64 padding character {@code '='} is accepted and
455 * interpreted as the end of the encoded byte data, but is not
456 * required. So if the final unit of the encoded byte data only has
457 * two or three Base64 characters (without the corresponding padding
458 * character(s) padded), they are decoded as if followed by padding
459 * character(s). If there is a padding character present in the
460 * final unit, the correct number of padding character(s) must be
461 * present, otherwise {@code IllegalArgumentException} (
462 * {@code IOException} when reading from a Base64 stream) is thrown
463 * during decoding.
464 *
465 * <p> Instances of {@link Decoder} class are safe for use by
466 * multiple concurrent threads.
467 *
468 * <p> Unless otherwise noted, passing a {@code null} argument to
469 * a method of this class will cause a
470 * {@link java.lang.NullPointerException NullPointerException} to
471 * be thrown.
472 *
473 * @see Encoder
474 * @since 1.8
475 */
476 public static class Decoder {
477
478 private final boolean isURL;
479 private final boolean isMIME;
480
481 private Decoder(boolean isURL, boolean isMIME) {
482 this.isURL = isURL;
483 this.isMIME = isMIME;
484 }
485
486 /**
487 * Lookup table for decoding unicode characters drawn from the
488 * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into
489 * their 6-bit positive integer equivalents. Characters that
490 * are not in the Base64 alphabet but fall within the bounds of
491 * the array are encoded to -1.
514
515 static final Decoder RFC4648 = new Decoder(false, false);
516 static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
517 static final Decoder RFC2045 = new Decoder(false, true);
518
519 /**
520 * Decodes all bytes from the input byte array using the {@link Base64}
521 * encoding scheme, writing the results into a newly-allocated output
522 * byte array. The returned byte array is of the length of the resulting
523 * bytes.
524 *
525 * @param src
526 * the byte array to decode
527 *
528 * @return A newly-allocated byte array containing the decoded bytes.
529 *
530 * @throws IllegalArgumentException
531 * if {@code src} is not in valid Base64 scheme
532 */
533 public byte[] decode(byte[] src) {
534 byte[] dst = new byte[outLength(src, 0, src.length)];
535 int ret = decode0(src, 0, src.length, dst);
536 if (ret != dst.length) {
537 dst = Arrays.copyOf(dst, ret);
538 }
539 return dst;
540 }
541
542 /**
543 * Decodes a Base64 encoded String into a newly-allocated byte array
544 * using the {@link Base64} encoding scheme.
545 *
546 * <p> An invocation of this method has exactly the same effect as invoking
547 * {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))}
548 *
549 * @param src
550 * the string to decode
551 *
552 * @return A newly-allocated byte array containing the decoded bytes.
553 *
554 * @throws IllegalArgumentException
567 * sure the output byte array {@code dst} has enough space for decoding
568 * all bytes from the input byte array. No bytes will be written to
569 * the output byte array if the output byte array is not big enough.
570 *
571 * <p> If the input byte array is not in valid Base64 encoding scheme
572 * then some bytes may have been written to the output byte array before
573 * IllegalargumentException is thrown.
574 *
575 * @param src
576 * the byte array to decode
577 * @param dst
578 * the output byte array
579 *
580 * @return The number of bytes written to the output byte array
581 *
582 * @throws IllegalArgumentException
583 * if {@code src} is not in valid Base64 scheme, or {@code dst}
584 * does not have enough space for decoding all input bytes.
585 */
586 public int decode(byte[] src, byte[] dst) {
587 int len = outLength(src, 0, src.length);
588 if (dst.length < len)
589 throw new IllegalArgumentException(
590 "Output byte array is too small for decoding all input bytes");
591 return decode0(src, 0, src.length, dst);
592 }
593
594 /**
595 * Decodes all bytes from the input byte buffer using the {@link Base64}
596 * encoding scheme, writing the results into a newly-allocated ByteBuffer.
597 *
598 * <p> Upon return, the source buffer's position will be updated to
599 * its limit; its limit will not have been changed. The returned
600 * output buffer's position will be zero and its limit will be the
601 * number of resulting decoded bytes
602 *
603 * <p> {@code IllegalArgumentException} is thrown if the input buffer
604 * is not in valid Base64 encoding scheme. The position of the input
605 * buffer will not be advanced in this case.
606 *
607 * @param buffer
608 * the ByteBuffer to decode
609 *
610 * @return A newly-allocated byte buffer containing the decoded bytes
611 *
612 * @throws IllegalArgumentException
613 * if {@code src} is not in valid Base64 scheme.
614 */
615 public ByteBuffer decode(ByteBuffer buffer) {
616 int pos0 = buffer.position();
617 try {
618 byte[] src;
619 int sp, sl;
620 if (buffer.hasArray()) {
621 src = buffer.array();
622 sp = buffer.arrayOffset() + buffer.position();
623 sl = buffer.arrayOffset() + buffer.limit();
624 buffer.position(buffer.limit());
625 } else {
626 src = new byte[buffer.remaining()];
627 buffer.get(src);
628 sp = 0;
629 sl = src.length;
630 }
631 byte[] dst = new byte[outLength(src, sp, sl)];
632 return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst));
633 } catch (IllegalArgumentException iae) {
634 buffer.position(pos0);
635 throw iae;
636 }
637 }
638
639 /**
640 * Returns an input stream for decoding {@link Base64} encoded byte stream.
641 *
642 * <p> The {@code read} methods of the returned {@code InputStream} will
643 * throw {@code IOException} when reading bytes that cannot be decoded.
644 *
645 * <p> Closing the returned input stream will close the underlying
646 * input stream.
647 *
648 * @param is
649 * the input stream
650 *
651 * @return the input stream for decoding the specified Base64 encoded
652 * byte stream
653 */
654 public InputStream wrap(InputStream is) {
655 Objects.requireNonNull(is);
656 return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
657 }
658
659 private int outLength(byte[] src, int sp, int sl) {
660 int[] base64 = isURL ? fromBase64URL : fromBase64;
661 int paddings = 0;
662 int len = sl - sp;
663 if (len == 0)
664 return 0;
665 if (len < 2) {
666 if (isMIME && base64[0] == -1)
667 return 0;
668 throw new IllegalArgumentException(
669 "Input byte[] should at least have 2 bytes for base64 bytes");
670 }
671 if (isMIME) {
672 // scan all bytes to fill out all non-alphabet. a performance
673 // trade-off of pre-scan or Arrays.copyOf
674 int n = 0;
675 while (sp < sl) {
676 int b = src[sp++] & 0xff;
677 if (b == '=') {
678 len -= (sl - sp + 1);
679 break;
680 }
681 if ((b = base64[b]) == -1)
682 n++;
683 }
684 len -= n;
685 } else {
686 if (src[sl - 1] == '=') {
687 paddings++;
688 if (src[sl - 2] == '=')
689 paddings++;
690 }
691 }
692 if (paddings == 0 && (len & 0x3) != 0)
693 paddings = 4 - (len & 0x3);
694 return 3 * ((len + 3) / 4) - paddings;
695 }
696
697 private int decode0(byte[] src, int sp, int sl, byte[] dst) {
698 int[] base64 = isURL ? fromBase64URL : fromBase64;
699 int dp = 0;
700 int bits = 0;
701 int shiftto = 18; // pos of first byte of 4-byte atom
702
703 while (sp < sl) {
704 if (shiftto == 18 && sp + 4 < sl) { // fast path
705 int sl0 = sp + ((sl - sp) & ~0b11);
706 while (sp < sl0) {
707 int b1 = base64[src[sp++] & 0xff];
708 int b2 = base64[src[sp++] & 0xff];
709 int b3 = base64[src[sp++] & 0xff];
710 int b4 = base64[src[sp++] & 0xff];
711 if ((b1 | b2 | b3 | b4) < 0) { // non base64 byte
712 sp -= 4;
713 break;
714 }
|
1 /*
2 * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.util;
27
28 import java.io.FilterOutputStream;
29 import java.io.InputStream;
30 import java.io.IOException;
31 import java.io.OutputStream;
32 import java.nio.ByteBuffer;
33 import java.nio.charset.CharacterCodingException;
34 import java.nio.charset.StandardCharsets;
35 import jdk.internal.HotSpotIntrinsicCandidate;
36
37 /**
38 * This class consists exclusively of static methods for obtaining
39 * encoders and decoders for the Base64 encoding scheme. The
40 * implementation of this class supports the following types of Base64
41 * as specified in
42 * <a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a> and
43 * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>.
44 *
45 * <ul>
46 * <li><a id="basic"><b>Basic</b></a>
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 * <li><a id="url"><b>URL and Filename safe</b></a>
170 * Returns a {@link Decoder} that decodes using the
171 * <a href="#mime">MIME</a> type base64 decoding scheme.
172 *
173 * @return A Base64 decoder.
174 */
175 public static Decoder getMimeDecoder() {
176 return Decoder.RFC2045;
177 }
178
179 /**
180 * This class implements an encoder for encoding byte data using
181 * the Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
182 *
183 * <p> Instances of {@link Encoder} class are safe for use by
184 * multiple concurrent threads.
185 *
186 * <p> Unless otherwise noted, passing a {@code null} argument to
187 * a method of this class will cause a
188 * {@link java.lang.NullPointerException NullPointerException} to
189 * be thrown.
190 * <p> If the encoded byte output of the needed size can not
191 * be allocated, the encode methods of this class will
192 * cause an {@link java.lang.OutOfMemoryError OutOfMemoryError}
193 * to be thrown.
194 *
195 * @see Decoder
196 * @since 1.8
197 */
198 public static class Encoder {
199
200 private final byte[] newline;
201 private final int linemax;
202 private final boolean isURL;
203 private final boolean doPadding;
204
205 private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) {
206 this.isURL = isURL;
207 this.newline = newline;
208 this.linemax = linemax;
209 this.doPadding = doPadding;
210 }
211
212 /**
213 * This array is a lookup table that translates 6-bit positive integer
225 /**
226 * It's the lookup table for "URL and Filename safe Base64" as specified
227 * in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
228 * '_'. This table is used when BASE64_URL is specified.
229 */
230 private static final char[] toBase64URL = {
231 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
232 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
233 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
234 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
235 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
236 };
237
238 private static final int MIMELINEMAX = 76;
239 private static final byte[] CRLF = new byte[] {'\r', '\n'};
240
241 static final Encoder RFC4648 = new Encoder(false, null, -1, true);
242 static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
243 static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
244
245 private final int outLength(int srclen, boolean withOutputParam) {
246 int len = 0;
247 try {
248 if (doPadding) {
249 len = Math.multiplyExact(4, (Math.addExact(srclen, 2) / 3));
250 } else {
251 int n = srclen % 3;
252 len = Math.addExact(Math.multiplyExact(4, (srclen / 3)), (n == 0 ? 0 : n + 1));
253 }
254 if (linemax > 0) { // line separators
255 len = Math.addExact(len, (len - 1) / linemax * newline.length);
256 }
257 } catch (ArithmeticException ex) {
258 if (!withOutputParam) {
259 throw new OutOfMemoryError("Encoded size is too large");
260 } else {
261 // let the caller know that encoded bytes can not fit
262 // in the passed output array param
263 len = -1;
264 }
265 }
266 return len;
267 }
268
269 /**
270 * Encodes all bytes from the specified byte array into a newly-allocated
271 * byte array using the {@link Base64} encoding scheme. The returned byte
272 * array is of the length of the resulting bytes.
273 *
274 * @param src
275 * the byte array to encode
276 * @return A newly-allocated byte array containing the resulting
277 * encoded bytes.
278 */
279 public byte[] encode(byte[] src) {
280 int len = outLength(src.length, false); // dst array size
281 byte[] dst = new byte[len];
282 int ret = encode0(src, 0, src.length, dst);
283 if (ret != dst.length)
284 return Arrays.copyOf(dst, ret);
285 return dst;
286 }
287
288 /**
289 * Encodes all bytes from the specified byte array using the
290 * {@link Base64} encoding scheme, writing the resulting bytes to the
291 * given output byte array, starting at offset 0.
292 *
293 * <p> It is the responsibility of the invoker of this method to make
294 * sure the output byte array {@code dst} has enough space for encoding
295 * all bytes from the input byte array. No bytes will be written to the
296 * output byte array if the output byte array is not big enough.
297 *
298 * @param src
299 * the byte array to encode
300 * @param dst
301 * the output byte array
302 * @return The number of bytes written to the output byte array
303 *
304 * @throws IllegalArgumentException if {@code dst} does not have enough
305 * space for encoding all input bytes.
306 */
307 public int encode(byte[] src, byte[] dst) {
308 int len = outLength(src.length, true); // dst array size
309 if (dst.length < len || len == -1)
310 throw new IllegalArgumentException(
311 "Output byte array is too small for encoding all input bytes");
312 return encode0(src, 0, src.length, dst);
313 }
314
315 /**
316 * Encodes the specified byte array into a String using the {@link Base64}
317 * encoding scheme.
318 *
319 * <p> This method first encodes all input bytes into a base64 encoded
320 * byte array and then constructs a new String by using the encoded byte
321 * array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1
322 * ISO-8859-1} charset.
323 *
324 * <p> In other words, an invocation of this method has exactly the same
325 * effect as invoking
326 * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.
327 *
328 * @param src
329 * the byte array to encode
330 * @return A String containing the resulting Base64 encoded characters
331 */
332 @SuppressWarnings("deprecation")
333 public String encodeToString(byte[] src) {
334 byte[] encoded = encode(src);
335 try {
336 return jdk.internal.access.SharedSecrets.getJavaLangAccess()
337 .newStringNoRepl(encoded, StandardCharsets.ISO_8859_1);
338 } catch(CharacterCodingException ex) {
339 // This exception never occurs, as there are
340 // no chances of encoded array being malformed
341 throw new RuntimeException("malformed encoded array", ex);
342 }
343 }
344
345 /**
346 * Encodes all remaining bytes from the specified byte buffer into
347 * a newly-allocated ByteBuffer using the {@link Base64} encoding
348 * scheme.
349 *
350 * Upon return, the source buffer's position will be updated to
351 * its limit; its limit will not have been changed. The returned
352 * output buffer's position will be zero and its limit will be the
353 * number of resulting encoded bytes.
354 *
355 * @param buffer
356 * the source ByteBuffer to encode
357 * @return A newly-allocated byte buffer containing the encoded bytes.
358 */
359 public ByteBuffer encode(ByteBuffer buffer) {
360 int len = outLength(buffer.remaining(), false);
361 byte[] dst = new byte[len];
362 int ret = 0;
363 if (buffer.hasArray()) {
364 ret = encode0(buffer.array(),
365 buffer.arrayOffset() + buffer.position(),
366 buffer.arrayOffset() + buffer.limit(),
367 dst);
368 buffer.position(buffer.limit());
369 } else {
370 byte[] src = new byte[buffer.remaining()];
371 buffer.get(src);
372 ret = encode0(src, 0, src.length, dst);
373 }
374 if (ret != dst.length)
375 dst = Arrays.copyOf(dst, ret);
376 return ByteBuffer.wrap(dst);
377 }
378
379 /**
380 * Wraps an output stream for encoding byte data using the {@link Base64}
475 * Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
476 *
477 * <p> The Base64 padding character {@code '='} is accepted and
478 * interpreted as the end of the encoded byte data, but is not
479 * required. So if the final unit of the encoded byte data only has
480 * two or three Base64 characters (without the corresponding padding
481 * character(s) padded), they are decoded as if followed by padding
482 * character(s). If there is a padding character present in the
483 * final unit, the correct number of padding character(s) must be
484 * present, otherwise {@code IllegalArgumentException} (
485 * {@code IOException} when reading from a Base64 stream) is thrown
486 * during decoding.
487 *
488 * <p> Instances of {@link Decoder} class are safe for use by
489 * multiple concurrent threads.
490 *
491 * <p> Unless otherwise noted, passing a {@code null} argument to
492 * a method of this class will cause a
493 * {@link java.lang.NullPointerException NullPointerException} to
494 * be thrown.
495 * <p> If the decoded byte output of the needed size can not
496 * be allocated, the decode methods of this class will
497 * cause an {@link java.lang.OutOfMemoryError OutOfMemoryError}
498 * to be thrown.
499 *
500 * @see Encoder
501 * @since 1.8
502 */
503 public static class Decoder {
504
505 private final boolean isURL;
506 private final boolean isMIME;
507
508 private Decoder(boolean isURL, boolean isMIME) {
509 this.isURL = isURL;
510 this.isMIME = isMIME;
511 }
512
513 /**
514 * Lookup table for decoding unicode characters drawn from the
515 * "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into
516 * their 6-bit positive integer equivalents. Characters that
517 * are not in the Base64 alphabet but fall within the bounds of
518 * the array are encoded to -1.
541
542 static final Decoder RFC4648 = new Decoder(false, false);
543 static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
544 static final Decoder RFC2045 = new Decoder(false, true);
545
546 /**
547 * Decodes all bytes from the input byte array using the {@link Base64}
548 * encoding scheme, writing the results into a newly-allocated output
549 * byte array. The returned byte array is of the length of the resulting
550 * bytes.
551 *
552 * @param src
553 * the byte array to decode
554 *
555 * @return A newly-allocated byte array containing the decoded bytes.
556 *
557 * @throws IllegalArgumentException
558 * if {@code src} is not in valid Base64 scheme
559 */
560 public byte[] decode(byte[] src) {
561 byte[] dst = new byte[outLength(src, 0, src.length, false)];
562 int ret = decode0(src, 0, src.length, dst);
563 if (ret != dst.length) {
564 dst = Arrays.copyOf(dst, ret);
565 }
566 return dst;
567 }
568
569 /**
570 * Decodes a Base64 encoded String into a newly-allocated byte array
571 * using the {@link Base64} encoding scheme.
572 *
573 * <p> An invocation of this method has exactly the same effect as invoking
574 * {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))}
575 *
576 * @param src
577 * the string to decode
578 *
579 * @return A newly-allocated byte array containing the decoded bytes.
580 *
581 * @throws IllegalArgumentException
594 * sure the output byte array {@code dst} has enough space for decoding
595 * all bytes from the input byte array. No bytes will be written to
596 * the output byte array if the output byte array is not big enough.
597 *
598 * <p> If the input byte array is not in valid Base64 encoding scheme
599 * then some bytes may have been written to the output byte array before
600 * IllegalargumentException is thrown.
601 *
602 * @param src
603 * the byte array to decode
604 * @param dst
605 * the output byte array
606 *
607 * @return The number of bytes written to the output byte array
608 *
609 * @throws IllegalArgumentException
610 * if {@code src} is not in valid Base64 scheme, or {@code dst}
611 * does not have enough space for decoding all input bytes.
612 */
613 public int decode(byte[] src, byte[] dst) {
614 int len = outLength(src, 0, src.length, true);
615 if (dst.length < len || len == -1)
616 throw new IllegalArgumentException(
617 "Output byte array is too small for decoding all input bytes");
618 return decode0(src, 0, src.length, dst);
619 }
620
621 /**
622 * Decodes all bytes from the input byte buffer using the {@link Base64}
623 * encoding scheme, writing the results into a newly-allocated ByteBuffer.
624 *
625 * <p> Upon return, the source buffer's position will be updated to
626 * its limit; its limit will not have been changed. The returned
627 * output buffer's position will be zero and its limit will be the
628 * number of resulting decoded bytes
629 *
630 * <p> {@code IllegalArgumentException} is thrown if the input buffer
631 * is not in valid Base64 encoding scheme. The position of the input
632 * buffer will not be advanced in this case.
633 *
634 * @param buffer
635 * the ByteBuffer to decode
636 *
637 * @return A newly-allocated byte buffer containing the decoded bytes
638 *
639 * @throws IllegalArgumentException
640 * if {@code buffer} is not in valid Base64 scheme
641 */
642 public ByteBuffer decode(ByteBuffer buffer) {
643 int pos0 = buffer.position();
644 try {
645 byte[] src;
646 int sp, sl;
647 if (buffer.hasArray()) {
648 src = buffer.array();
649 sp = buffer.arrayOffset() + buffer.position();
650 sl = buffer.arrayOffset() + buffer.limit();
651 buffer.position(buffer.limit());
652 } else {
653 src = new byte[buffer.remaining()];
654 buffer.get(src);
655 sp = 0;
656 sl = src.length;
657 }
658 byte[] dst = new byte[outLength(src, sp, sl, false)];
659 return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst));
660 } catch (IllegalArgumentException iae) {
661 buffer.position(pos0);
662 throw iae;
663 }
664 }
665
666 /**
667 * Returns an input stream for decoding {@link Base64} encoded byte stream.
668 *
669 * <p> The {@code read} methods of the returned {@code InputStream} will
670 * throw {@code IOException} when reading bytes that cannot be decoded.
671 *
672 * <p> Closing the returned input stream will close the underlying
673 * input stream.
674 *
675 * @param is
676 * the input stream
677 *
678 * @return the input stream for decoding the specified Base64 encoded
679 * byte stream
680 */
681 public InputStream wrap(InputStream is) {
682 Objects.requireNonNull(is);
683 return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
684 }
685
686 private int outLength(byte[] src, int sp, int sl, boolean withOutputParam) {
687 int[] base64 = isURL ? fromBase64URL : fromBase64;
688 int paddings = 0;
689 int len = sl - sp;
690 if (len == 0)
691 return 0;
692 if (len < 2) {
693 if (isMIME && base64[0] == -1)
694 return 0;
695 throw new IllegalArgumentException(
696 "Input byte[] should at least have 2 bytes for base64 bytes");
697 }
698 if (isMIME) {
699 // scan all bytes to fill out all non-alphabet. a performance
700 // trade-off of pre-scan or Arrays.copyOf
701 int n = 0;
702 while (sp < sl) {
703 int b = src[sp++] & 0xff;
704 if (b == '=') {
705 len -= (sl - sp + 1);
706 break;
707 }
708 if ((b = base64[b]) == -1)
709 n++;
710 }
711 len -= n;
712 } else {
713 if (src[sl - 1] == '=') {
714 paddings++;
715 if (src[sl - 2] == '=')
716 paddings++;
717 }
718 }
719 if (paddings == 0 && (len & 0x3) != 0)
720 paddings = 4 - (len & 0x3);
721
722 try {
723 len = Math.multiplyExact(3, (Math.addExact(len, 3) / 4)) - paddings;
724 } catch (ArithmeticException ex) {
725 if (!withOutputParam) {
726 throw new OutOfMemoryError("Decoded size is too large");
727 } else {
728 // let the caller know that the decoded bytes can not
729 // fit in the passed output array param
730 len = -1;
731 }
732 }
733 return len;
734 }
735
736 private int decode0(byte[] src, int sp, int sl, byte[] dst) {
737 int[] base64 = isURL ? fromBase64URL : fromBase64;
738 int dp = 0;
739 int bits = 0;
740 int shiftto = 18; // pos of first byte of 4-byte atom
741
742 while (sp < sl) {
743 if (shiftto == 18 && sp + 4 < sl) { // fast path
744 int sl0 = sp + ((sl - sp) & ~0b11);
745 while (sp < sl0) {
746 int b1 = base64[src[sp++] & 0xff];
747 int b2 = base64[src[sp++] & 0xff];
748 int b3 = base64[src[sp++] & 0xff];
749 int b4 = base64[src[sp++] & 0xff];
750 if ((b1 | b2 | b3 | b4) < 0) { // non base64 byte
751 sp -= 4;
752 break;
753 }
|