334 int len = outLength(buffer.remaining()); 335 byte[] dst = new byte[len]; 336 int ret = 0; 337 if (buffer.hasArray()) { 338 ret = encode0(buffer.array(), 339 buffer.arrayOffset() + buffer.position(), 340 buffer.arrayOffset() + buffer.limit(), 341 dst); 342 buffer.position(buffer.limit()); 343 } else { 344 byte[] src = new byte[buffer.remaining()]; 345 buffer.get(src); 346 ret = encode0(src, 0, src.length, dst); 347 } 348 if (ret != dst.length) 349 dst = Arrays.copyOf(dst, ret); 350 return ByteBuffer.wrap(dst); 351 } 352 353 /** 354 * Encodes as many bytes as possible from the input byte buffer 355 * using the {@link Base64} encoding scheme, writing the resulting 356 * bytes to the given output byte buffer. 357 * 358 * <p>The buffers are read from, and written to, starting at their 359 * current positions. Upon return, the input and output buffers' 360 * positions will be advanced to reflect the bytes read and written, 361 * but their limits will not be modified. 362 * 363 * <p>The encoding operation will stop and return if either all 364 * remaining bytes in the input buffer have been encoded and written 365 * to the output buffer, or the output buffer has insufficient space 366 * to encode any more input bytes. The encoding operation can be 367 * continued, if there is more bytes in input buffer to be encoded, 368 * by invoking this method again with an output buffer that has more 369 * {@linkplain java.nio.Buffer#remaining remaining} bytes. This is 370 * typically done by draining any encoded bytes from the output buffer. 371 * The value returned from last invocation needs to be passed in as the 372 * third parameter {@code bytesOut} if it is to continue an unfinished 373 * encoding, 0 otherwise. 374 * 375 * <p><b>Recommended Usage Example</b> 376 * <pre> 377 * ByteBuffer src = ...; 378 * ByteBuffer dst = ...; 379 * Base64.Encoder enc = Base64.getMimeDecoder(); 380 * 381 * int bytesOut = 0; 382 * while (src.hasRemaining()) { 383 * // clear output buffer for decoding 384 * dst.clear(); 385 * bytesOut = enc.encode(src, dst, bytesOut); 386 * 387 * // read encoded bytes out of "dst" 388 * dst.flip(); 389 * ... 390 * } 391 * </pre> 392 * 393 * @param src 394 * the input byte buffer to encode 395 * @param dst 396 * the output byte buffer 397 * @param bytesOut 398 * the return value of last invocation if this is to continue 399 * an unfinished encoding operation, 0 otherwise 400 * @return The sum total of {@code bytesOut} and the number of bytes 401 * written to the output ByteBuffer during this invocation. 402 */ 403 public int encode(ByteBuffer src, ByteBuffer dst, int bytesOut) { 404 if (src.hasArray() && dst.hasArray()) 405 return encodeArray(src, dst, bytesOut); 406 return encodeBuffer(src, dst, bytesOut); 407 } 408 409 /** 410 * Wraps an output stream for encoding byte data using the {@link Base64} 411 * encoding scheme. 412 * 413 * <p> It is recommended to promptly close the returned output stream after 414 * use, during which it will flush all possible leftover bytes to the underlying 415 * output stream. Closing the returned output stream will close the underlying 416 * output stream. 417 * 418 * @param os 419 * the output stream. 420 * @return the output stream for encoding the byte data into the 421 * specified Base64 encoded format 422 */ 423 public OutputStream wrap(OutputStream os) { 424 Objects.requireNonNull(os); 425 return new EncOutputStream(os, isURL ? toBase64URL : toBase64, 426 newline, linemax, doPadding); 427 } 428 429 /** 430 * Returns an encoder instance that encodes equivalently to this one, 431 * but without adding any padding character at the end of the encoded 432 * byte data. 433 * 434 * <p> The encoding scheme of this encoder instance is unaffected by 435 * this invocation. The returned encoder instance should be used for 436 * non-padding encoding operation. 437 * 438 * @return an equivalent encoder that encodes without adding any 439 * padding character at the end 440 */ 441 public Encoder withoutPadding() { 442 if (!doPadding) 443 return this; 444 return new Encoder(isURL, newline, linemax, false); 445 } 446 447 private int encodeArray(ByteBuffer src, ByteBuffer dst, int bytesOut) { 448 char[] base64 = isURL? toBase64URL : toBase64; 449 byte[] sa = src.array(); 450 int sp = src.arrayOffset() + src.position(); 451 int sl = src.arrayOffset() + src.limit(); 452 byte[] da = dst.array(); 453 int dp = dst.arrayOffset() + dst.position(); 454 int dl = dst.arrayOffset() + dst.limit(); 455 int dp00 = dp; 456 int dpos = 0; // dp of each line 457 if (linemax > 0 && bytesOut > 0) 458 dpos = bytesOut % (linemax + newline.length); 459 try { 460 if (dpos == linemax && sp < src.limit()) { 461 if (dp + newline.length > dl) 462 return dp - dp00 + bytesOut; 463 for (byte b : newline){ 464 dst.put(dp++, b); 465 } 466 dpos = 0; 467 } 468 sl = sp + (sl - sp) / 3 * 3; 469 while (sp < sl) { 470 int slen = (linemax > 0) ? (linemax - dpos) / 4 * 3 471 : sl - sp; 472 int sl0 = Math.min(sp + slen, sl); 473 for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) { 474 if (dp0 + 4 > dl) { 475 sp = sp0; dp = dp0; 476 return dp0 - dp00 + bytesOut; 477 } 478 int bits = (sa[sp0++] & 0xff) << 16 | 479 (sa[sp0++] & 0xff) << 8 | 480 (sa[sp0++] & 0xff); 481 da[dp0++] = (byte)base64[(bits >>> 18) & 0x3f]; 482 da[dp0++] = (byte)base64[(bits >>> 12) & 0x3f]; 483 da[dp0++] = (byte)base64[(bits >>> 6) & 0x3f]; 484 da[dp0++] = (byte)base64[bits & 0x3f]; 485 } 486 int n = (sl0 - sp) / 3 * 4; 487 dpos += n; 488 dp += n; 489 sp = sl0; 490 if (dpos == linemax && sp < src.limit()) { 491 if (dp + newline.length > dl) 492 return dp - dp00 + bytesOut; 493 for (byte b : newline){ 494 da[dp++] = b; 495 } 496 dpos = 0; 497 } 498 } 499 sl = src.arrayOffset() + src.limit(); 500 if (sp < sl && dl >= dp + 4) { // 1 or 2 leftover bytes 501 int b0 = sa[sp++] & 0xff; 502 da[dp++] = (byte)base64[b0 >> 2]; 503 if (sp == sl) { 504 da[dp++] = (byte)base64[(b0 << 4) & 0x3f]; 505 if (doPadding) { 506 da[dp++] = '='; 507 da[dp++] = '='; 508 } 509 } else { 510 int b1 = sa[sp++] & 0xff; 511 da[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)]; 512 da[dp++] = (byte)base64[(b1 << 2) & 0x3f]; 513 if (doPadding) { 514 da[dp++] = '='; 515 } 516 } 517 } 518 return dp - dp00 + bytesOut; 519 } finally { 520 src.position(sp - src.arrayOffset()); 521 dst.position(dp - dst.arrayOffset()); 522 } 523 } 524 525 private int encodeBuffer(ByteBuffer src, ByteBuffer dst, int bytesOut) { 526 char[] base64 = isURL? toBase64URL : toBase64; 527 int sp = src.position(); 528 int sl = src.limit(); 529 int dp = dst.position(); 530 int dl = dst.limit(); 531 int dp00 = dp; 532 533 int dpos = 0; // dp of each line 534 if (linemax > 0 && bytesOut > 0) 535 dpos = bytesOut % (linemax + newline.length); 536 try { 537 if (dpos == linemax && sp < src.limit()) { 538 if (dp + newline.length > dl) 539 return dp - dp00 + bytesOut; 540 for (byte b : newline){ 541 dst.put(dp++, b); 542 } 543 dpos = 0; 544 } 545 sl = sp + (sl - sp) / 3 * 3; 546 while (sp < sl) { 547 int slen = (linemax > 0) ? (linemax - dpos) / 4 * 3 548 : sl - sp; 549 int sl0 = Math.min(sp + slen, sl); 550 for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) { 551 if (dp0 + 4 > dl) { 552 sp = sp0; dp = dp0; 553 return dp0 - dp00 + bytesOut; 554 } 555 int bits = (src.get(sp0++) & 0xff) << 16 | 556 (src.get(sp0++) & 0xff) << 8 | 557 (src.get(sp0++) & 0xff); 558 dst.put(dp0++, (byte)base64[(bits >>> 18) & 0x3f]); 559 dst.put(dp0++, (byte)base64[(bits >>> 12) & 0x3f]); 560 dst.put(dp0++, (byte)base64[(bits >>> 6) & 0x3f]); 561 dst.put(dp0++, (byte)base64[bits & 0x3f]); 562 } 563 int n = (sl0 - sp) / 3 * 4; 564 dpos += n; 565 dp += n; 566 sp = sl0; 567 if (dpos == linemax && sp < src.limit()) { 568 if (dp + newline.length > dl) 569 return dp - dp00 + bytesOut; 570 for (byte b : newline){ 571 dst.put(dp++, b); 572 } 573 dpos = 0; 574 } 575 } 576 if (sp < src.limit() && dl >= dp + 4) { // 1 or 2 leftover bytes 577 int b0 = src.get(sp++) & 0xff; 578 dst.put(dp++, (byte)base64[b0 >> 2]); 579 if (sp == src.limit()) { 580 dst.put(dp++, (byte)base64[(b0 << 4) & 0x3f]); 581 if (doPadding) { 582 dst.put(dp++, (byte)'='); 583 dst.put(dp++, (byte)'='); 584 } 585 } else { 586 int b1 = src.get(sp++) & 0xff; 587 dst.put(dp++, (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)]); 588 dst.put(dp++, (byte)base64[(b1 << 2) & 0x3f]); 589 if (doPadding) { 590 dst.put(dp++, (byte)'='); 591 } 592 } 593 } 594 return dp - dp00 + bytesOut; 595 } finally { 596 src.position(sp); 597 dst.position(dp); 598 } 599 } 600 601 private int encode0(byte[] src, int off, int end, byte[] dst) { 602 char[] base64 = isURL ? toBase64URL : toBase64; 603 int sp = off; 604 int slen = (end - off) / 3 * 3; 605 int sl = off + slen; 606 if (linemax > 0 && slen > linemax / 4 * 3) 607 slen = linemax / 4 * 3; 608 int dp = 0; 609 while (sp < sl) { 610 int sl0 = Math.min(sp + slen, sl); 611 for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) { 612 int bits = (src[sp0++] & 0xff) << 16 | 613 (src[sp0++] & 0xff) << 8 | 614 (src[sp0++] & 0xff); 615 dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f]; 616 dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f]; 617 dst[dp0++] = (byte)base64[(bits >>> 6) & 0x3f]; 618 dst[dp0++] = (byte)base64[bits & 0x3f]; 619 } 620 int dlen = (sl0 - sp) / 3 * 4; 640 dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)]; 641 dst[dp++] = (byte)base64[(b1 << 2) & 0x3f]; 642 if (doPadding) { 643 dst[dp++] = '='; 644 } 645 } 646 } 647 return dp; 648 } 649 } 650 651 /** 652 * This class implements a decoder for decoding byte data using the 653 * Base64 encoding scheme as specified in RFC 4648 and RFC 2045. 654 * 655 * <p> The Base64 padding character {@code '='} is accepted and 656 * interpreted as the end of the encoded byte data, but is not 657 * required. So if the final unit of the encoded byte data only has 658 * two or three Base64 characters (without the corresponding padding 659 * character(s) padded), they are decoded as if followed by padding 660 * character(s). 661 * <p> 662 * For decoders that use the <a href="#basic">Basic</a> and 663 * <a href="#url">URL and Filename safe</a> type base64 scheme, and 664 * if there is padding character present in the final unit, the 665 * correct number of padding character(s) must be present, otherwise 666 * {@code IllegalArgumentException} ({@code IOException} when reading 667 * from a Base64 stream) is thrown during decoding. 668 * <p> 669 * Decoders that use the <a href="#mime">MIME</a> type base64 scheme 670 * are more lenient when decoding the padding character(s). If the 671 * padding character(s) is incorrectly encoded, the first padding 672 * character encountered is interpreted as the end of the encoded byte 673 * data, the decoding operation will then end and return normally. 674 * 675 * <p> Instances of {@link Decoder} class are safe for use by 676 * multiple concurrent threads. 677 * 678 * <p> Unless otherwise noted, passing a {@code null} argument to 679 * a method of this class will cause a 680 * {@link java.lang.NullPointerException NullPointerException} to 681 * be thrown. 682 * 683 * @see Encoder 684 * @since 1.8 685 */ 686 public static class Decoder { 687 688 private final boolean isURL; 689 private final boolean isMIME; 690 691 private Decoder(boolean isURL, boolean isMIME) { 692 this.isURL = isURL; 693 this.isMIME = isMIME; 793 * if {@code src} is not in valid Base64 scheme, or {@code dst} 794 * does not have enough space for decoding all input bytes. 795 */ 796 public int decode(byte[] src, byte[] dst) { 797 int len = outLength(src, 0, src.length); 798 if (dst.length < len) 799 throw new IllegalArgumentException( 800 "Output byte array is too small for decoding all input bytes"); 801 return decode0(src, 0, src.length, dst); 802 } 803 804 /** 805 * Decodes all bytes from the input byte buffer using the {@link Base64} 806 * encoding scheme, writing the results into a newly-allocated ByteBuffer. 807 * 808 * <p> Upon return, the source buffer's position will be updated to 809 * its limit; its limit will not have been changed. The returned 810 * output buffer's position will be zero and its limit will be the 811 * number of resulting decoded bytes 812 * 813 * @param buffer 814 * the ByteBuffer to decode 815 * 816 * @return A newly-allocated byte buffer containing the decoded bytes 817 * 818 * @throws IllegalArgumentException 819 * if {@code src} is not in valid Base64 scheme. 820 */ 821 public ByteBuffer decode(ByteBuffer buffer) { 822 int pos0 = buffer.position(); 823 try { 824 byte[] src; 825 int sp, sl; 826 if (buffer.hasArray()) { 827 src = buffer.array(); 828 sp = buffer.arrayOffset() + buffer.position(); 829 sl = buffer.arrayOffset() + buffer.limit(); 830 buffer.position(buffer.limit()); 831 } else { 832 src = new byte[buffer.remaining()]; 833 buffer.get(src); 834 sp = 0; 835 sl = src.length; 836 } 837 byte[] dst = new byte[outLength(src, sp, sl)]; 838 return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst)); 839 } catch (IllegalArgumentException iae) { 840 buffer.position(pos0); 841 throw iae; 842 } 843 } 844 845 /** 846 * Decodes as many bytes as possible from the input byte buffer 847 * using the {@link Base64} encoding scheme, writing the resulting 848 * bytes to the given output byte buffer. 849 * 850 * <p>The buffers are read from, and written to, starting at their 851 * current positions. Upon return, the input and output buffers' 852 * positions will be advanced to reflect the bytes read and written, 853 * but their limits will not be modified. 854 * 855 * <p> If the input buffer is not in valid Base64 encoding scheme 856 * then some bytes may have been written to the output buffer 857 * before IllegalArgumentException is thrown. The positions of 858 * both input and output buffer will not be advanced in this case. 859 * 860 * <p>The decoding operation will end and return if all remaining 861 * bytes in the input buffer have been decoded and written to the 862 * output buffer. 863 * 864 * <p> The decoding operation will stop and return if the output 865 * buffer has insufficient space to decode any more input bytes. 866 * The decoding operation can be continued, if there is more bytes 867 * in input buffer to be decoded, by invoking this method again with 868 * an output buffer that has more {@linkplain java.nio.Buffer#remaining 869 * remaining} bytes. This is typically done by draining any decoded 870 * bytes from the output buffer. 871 * 872 * <p><b>Recommended Usage Example</b> 873 * <pre> 874 * ByteBuffer src = ...; 875 * ByteBuffer dst = ...; 876 * Base64.Decoder dec = Base64.getDecoder(); 877 * 878 * while (src.hasRemaining()) { 879 * 880 * // prepare the output byte buffer 881 * dst.clear(); 882 * dec.decode(src, dst); 883 * 884 * // read bytes from the output buffer 885 * dst.flip(); 886 * ... 887 * } 888 * </pre> 889 * 890 * @param src 891 * the input byte buffer to decode 892 * @param dst 893 * the output byte buffer 894 * 895 * @return The number of bytes written to the output byte buffer during 896 * this decoding invocation 897 * 898 * @throws IllegalArgumentException 899 * if {@code src} is not in valid Base64 scheme. 900 */ 901 public int decode(ByteBuffer src, ByteBuffer dst) { 902 int sp0 = src.position(); 903 int dp0 = dst.position(); 904 try { 905 if (src.hasArray() && dst.hasArray()) 906 return decodeArray(src, dst); 907 return decodeBuffer(src, dst); 908 } catch (IllegalArgumentException iae) { 909 src.position(sp0); 910 dst.position(dp0); 911 throw iae; 912 } 913 } 914 915 /** 916 * Returns an input stream for decoding {@link Base64} encoded byte stream. 917 * 918 * <p> The {@code read} methods of the returned {@code InputStream} will 919 * throw {@code IOException} when reading bytes that cannot be decoded. 920 * 921 * <p> Closing the returned input stream will close the underlying 922 * input stream. 923 * 924 * @param is 925 * the input stream 926 * 927 * @return the input stream for decoding the specified Base64 encoded 928 * byte stream 929 */ 930 public InputStream wrap(InputStream is) { 931 Objects.requireNonNull(is); 932 return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME); 933 } 934 935 private int decodeArray(ByteBuffer src, ByteBuffer dst) { 936 int[] base64 = isURL ? fromBase64URL : fromBase64; 937 int bits = 0; 938 int shiftto = 18; // pos of first byte of 4-byte atom 939 byte[] sa = src.array(); 940 int sp = src.arrayOffset() + src.position(); 941 int sl = src.arrayOffset() + src.limit(); 942 byte[] da = dst.array(); 943 int dp = dst.arrayOffset() + dst.position(); 944 int dl = dst.arrayOffset() + dst.limit(); 945 int dp0 = dp; 946 int mark = sp; 947 try { 948 while (sp < sl) { 949 int b = sa[sp++] & 0xff; 950 if ((b = base64[b]) < 0) { 951 if (b == -2) { // padding byte 952 if (!isMIME && 953 (shiftto == 6 && (sp == sl || sa[sp++] != '=') || 954 shiftto == 18)) { 955 throw new IllegalArgumentException( 956 "Input byte array has wrong 4-byte ending unit"); 957 } 958 break; 959 } 960 if (isMIME) // skip if for rfc2045 961 continue; 962 else 963 throw new IllegalArgumentException( 964 "Illegal base64 character " + 965 Integer.toString(sa[sp - 1], 16)); 966 } 967 bits |= (b << shiftto); 968 shiftto -= 6; 969 if (shiftto < 0) { 970 if (dl < dp + 3) 971 return dp - dp0; 972 da[dp++] = (byte)(bits >> 16); 973 da[dp++] = (byte)(bits >> 8); 974 da[dp++] = (byte)(bits); 975 shiftto = 18; 976 bits = 0; 977 mark = sp; 978 } 979 } 980 if (shiftto == 6) { 981 if (dl - dp < 1) 982 return dp - dp0; 983 da[dp++] = (byte)(bits >> 16); 984 } else if (shiftto == 0) { 985 if (dl - dp < 2) 986 return dp - dp0; 987 da[dp++] = (byte)(bits >> 16); 988 da[dp++] = (byte)(bits >> 8); 989 } else if (shiftto == 12) { 990 throw new IllegalArgumentException( 991 "Last unit does not have enough valid bits"); 992 } 993 if (sp < sl) { 994 if (isMIME) 995 sp = sl; 996 else 997 throw new IllegalArgumentException( 998 "Input byte array has incorrect ending byte at " + sp); 999 } 1000 mark = sp; 1001 return dp - dp0; 1002 } finally { 1003 src.position(mark); 1004 dst.position(dp); 1005 } 1006 } 1007 1008 private int decodeBuffer(ByteBuffer src, ByteBuffer dst) { 1009 int[] base64 = isURL ? fromBase64URL : fromBase64; 1010 int bits = 0; 1011 int shiftto = 18; // pos of first byte of 4-byte atom 1012 int sp = src.position(); 1013 int sl = src.limit(); 1014 int dp = dst.position(); 1015 int dl = dst.limit(); 1016 int dp0 = dp; 1017 int mark = sp; 1018 try { 1019 while (sp < sl) { 1020 int b = src.get(sp++) & 0xff; 1021 if ((b = base64[b]) < 0) { 1022 if (b == -2) { // padding byte 1023 if (!isMIME && 1024 (shiftto == 6 && (sp == sl || src.get(sp++) != '=') || 1025 shiftto == 18)) { 1026 throw new IllegalArgumentException( 1027 "Input byte array has wrong 4-byte ending unit"); 1028 } 1029 break; 1030 } 1031 if (isMIME) // skip if for rfc2045 1032 continue; 1033 else 1034 throw new IllegalArgumentException( 1035 "Illegal base64 character " + 1036 Integer.toString(src.get(sp - 1), 16)); 1037 } 1038 bits |= (b << shiftto); 1039 shiftto -= 6; 1040 if (shiftto < 0) { 1041 if (dl < dp + 3) 1042 return dp - dp0; 1043 dst.put(dp++, (byte)(bits >> 16)); 1044 dst.put(dp++, (byte)(bits >> 8)); 1045 dst.put(dp++, (byte)(bits)); 1046 shiftto = 18; 1047 bits = 0; 1048 mark = sp; 1049 } 1050 } 1051 if (shiftto == 6) { 1052 if (dl - dp < 1) 1053 return dp - dp0; 1054 dst.put(dp++, (byte)(bits >> 16)); 1055 } else if (shiftto == 0) { 1056 if (dl - dp < 2) 1057 return dp - dp0; 1058 dst.put(dp++, (byte)(bits >> 16)); 1059 dst.put(dp++, (byte)(bits >> 8)); 1060 } else if (shiftto == 12) { 1061 throw new IllegalArgumentException( 1062 "Last unit does not have enough valid bits"); 1063 } 1064 if (sp < sl) { 1065 if (isMIME) 1066 sp = sl; 1067 else 1068 throw new IllegalArgumentException( 1069 "Input byte array has incorrect ending byte at " + sp); 1070 } 1071 mark = sp; 1072 return dp - dp0; 1073 } finally { 1074 src.position(mark); 1075 dst.position(dp); 1076 } 1077 } 1078 1079 private int outLength(byte[] src, int sp, int sl) { 1080 int[] base64 = isURL ? fromBase64URL : fromBase64; 1081 int paddings = 0; 1082 int len = sl - sp; 1083 if (len == 0) 1084 return 0; 1085 if (len < 2) { 1086 if (isMIME && base64[0] == -1) 1087 return 0; 1088 throw new IllegalArgumentException( 1089 "Input byte[] should at least have 2 bytes for base64 bytes"); 1090 } 1091 if (isMIME) { 1092 // scan all bytes to fill out all non-alphabet. a performance 1093 // trade-off of pre-scan or Arrays.copyOf 1094 int n = 0; 1095 while (sp < sl) { 1096 int b = src[sp++] & 0xff; 1097 if (b == '=') { 1098 len -= (sl - sp + 1); 1106 if (src[sl - 1] == '=') { 1107 paddings++; 1108 if (src[sl - 2] == '=') 1109 paddings++; 1110 } 1111 } 1112 if (paddings == 0 && (len & 0x3) != 0) 1113 paddings = 4 - (len & 0x3); 1114 return 3 * ((len + 3) / 4) - paddings; 1115 } 1116 1117 private int decode0(byte[] src, int sp, int sl, byte[] dst) { 1118 int[] base64 = isURL ? fromBase64URL : fromBase64; 1119 int dp = 0; 1120 int bits = 0; 1121 int shiftto = 18; // pos of first byte of 4-byte atom 1122 while (sp < sl) { 1123 int b = src[sp++] & 0xff; 1124 if ((b = base64[b]) < 0) { 1125 if (b == -2) { // padding byte '=' 1126 if (!isMIME && // be lenient for rfc2045 1127 // = shiftto==18 unnecessary padding 1128 // x= shiftto==12 a dangling single x 1129 // x to be handled together with non-padding case 1130 // xx= shiftto==6&&sp==sl missing last = 1131 // xx=y shiftto==6 last is not = 1132 (shiftto == 6 && (sp == sl || src[sp++] != '=') || 1133 shiftto == 18)) { 1134 throw new IllegalArgumentException( 1135 "Input byte array has wrong 4-byte ending unit"); 1136 } 1137 break; 1138 } 1139 if (isMIME) // skip if for rfc2045 1140 continue; 1141 else 1142 throw new IllegalArgumentException( 1143 "Illegal base64 character " + 1144 Integer.toString(src[sp - 1], 16)); 1145 } 1146 bits |= (b << shiftto); 1147 shiftto -= 6; 1148 if (shiftto < 0) { 1149 dst[dp++] = (byte)(bits >> 16); 1150 dst[dp++] = (byte)(bits >> 8); 1151 dst[dp++] = (byte)(bits); 1152 shiftto = 18; 1153 bits = 0; 1154 } 1155 } 1156 // reached end of byte array or hit padding '=' characters. 1157 if (shiftto == 6) { 1158 dst[dp++] = (byte)(bits >> 16); 1159 } else if (shiftto == 0) { 1160 dst[dp++] = (byte)(bits >> 16); 1161 dst[dp++] = (byte)(bits >> 8); 1162 } else if (shiftto == 12) { 1163 // dangling single "x", throw exception even in lenient mode, 1164 // it's incorrectly encoded. 1165 throw new IllegalArgumentException( 1166 "Last unit does not have enough valid bits"); 1167 } 1168 // anything left is invalid, if is not MIME. 1169 // if MIME (lenient), just ignore all leftover 1170 if (sp < sl && !isMIME) { 1171 throw new IllegalArgumentException( 1172 "Input byte array has incorrect ending byte at " + sp); 1173 } 1174 return dp; 1175 } 1176 } 1177 1178 /* 1179 * An output stream for encoding bytes into the Base64. 1180 */ 1181 private static class EncOutputStream extends FilterOutputStream { 1182 1183 private int leftover = 0; 1184 private int b0, b1, b2; 1185 private boolean closed = false; 1186 1187 private final char[] base64; // byte->base64 mapping 1188 private final byte[] newline; // line separator, if needed 1189 private final int linemax; 1190 private final boolean doPadding;// whether or not to pad 1350 if (len == 0) { // no enough output space 1351 bits >>= 8; // shift to lowest byte 1352 nextout = 0; 1353 } else { 1354 b[off++] = (byte) (bits >> 8); 1355 } 1356 } 1357 } 1358 if (off == oldOff) 1359 return -1; 1360 else 1361 return off - oldOff; 1362 } 1363 if (v == '=') { // padding byte(s) 1364 // = shiftto==18 unnecessary padding 1365 // x= shiftto==12 dangling x, invalid unit 1366 // xx= shiftto==6 && missing last '=' 1367 // xx=y or last is not '=' 1368 if (nextin == 18 || nextin == 12 || 1369 nextin == 6 && is.read() != '=') { 1370 if (!isMIME || nextin == 12) { 1371 throw new IOException("Illegal base64 ending sequence:" + nextin); 1372 } else if (nextin != 18) { 1373 // lenient mode for mime 1374 // (1) handle the "unnecessary padding in "xxxx =" 1375 // case as the eof (nextin == 18) 1376 // (2) decode "xx=" and "xx=y" normally 1377 b[off++] = (byte)(bits >> (16)); 1378 len--; 1379 } 1380 } else { 1381 b[off++] = (byte)(bits >> (16)); 1382 len--; 1383 if (nextin == 0) { // only one padding byte 1384 if (len == 0) { // no enough output space 1385 bits >>= 8; // shift to lowest byte 1386 nextout = 0; 1387 } else { 1388 b[off++] = (byte) (bits >> 8); 1389 } 1390 } 1391 } 1392 eof = true; 1393 break; 1394 } 1395 if ((v = base64[v]) == -1) { 1396 if (isMIME) // skip if for rfc2045 1397 continue; 1398 else 1399 throw new IOException("Illegal base64 character " + 1400 Integer.toString(v, 16)); 1401 } 1402 bits |= (v << nextin); 1403 if (nextin == 0) { 1404 nextin = 18; // clear for next 1405 nextout = 16; 1406 while (nextout >= 0) { 1407 b[off++] = (byte)(bits >> nextout); 1408 len--; 1409 nextout -= 8; 1410 if (len == 0 && nextout >= 0) { // don't clean "bits" 1411 return off - oldOff; | 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 * Wraps an output stream for encoding byte data using the {@link Base64} 355 * encoding scheme. 356 * 357 * <p> It is recommended to promptly close the returned output stream after 358 * use, during which it will flush all possible leftover bytes to the underlying 359 * output stream. Closing the returned output stream will close the underlying 360 * output stream. 361 * 362 * @param os 363 * the output stream. 364 * @return the output stream for encoding the byte data into the 365 * specified Base64 encoded format 366 */ 367 public OutputStream wrap(OutputStream os) { 368 Objects.requireNonNull(os); 369 return new EncOutputStream(os, isURL ? toBase64URL : toBase64, 370 newline, linemax, doPadding); 371 } 372 373 /** 374 * Returns an encoder instance that encodes equivalently to this one, 375 * but without adding any padding character at the end of the encoded 376 * byte data. 377 * 378 * <p> The encoding scheme of this encoder instance is unaffected by 379 * this invocation. The returned encoder instance should be used for 380 * non-padding encoding operation. 381 * 382 * @return an equivalent encoder that encodes without adding any 383 * padding character at the end 384 */ 385 public Encoder withoutPadding() { 386 if (!doPadding) 387 return this; 388 return new Encoder(isURL, newline, linemax, false); 389 } 390 391 private int encode0(byte[] src, int off, int end, byte[] dst) { 392 char[] base64 = isURL ? toBase64URL : toBase64; 393 int sp = off; 394 int slen = (end - off) / 3 * 3; 395 int sl = off + slen; 396 if (linemax > 0 && slen > linemax / 4 * 3) 397 slen = linemax / 4 * 3; 398 int dp = 0; 399 while (sp < sl) { 400 int sl0 = Math.min(sp + slen, sl); 401 for (int sp0 = sp, dp0 = dp ; sp0 < sl0; ) { 402 int bits = (src[sp0++] & 0xff) << 16 | 403 (src[sp0++] & 0xff) << 8 | 404 (src[sp0++] & 0xff); 405 dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f]; 406 dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f]; 407 dst[dp0++] = (byte)base64[(bits >>> 6) & 0x3f]; 408 dst[dp0++] = (byte)base64[bits & 0x3f]; 409 } 410 int dlen = (sl0 - sp) / 3 * 4; 430 dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)]; 431 dst[dp++] = (byte)base64[(b1 << 2) & 0x3f]; 432 if (doPadding) { 433 dst[dp++] = '='; 434 } 435 } 436 } 437 return dp; 438 } 439 } 440 441 /** 442 * This class implements a decoder for decoding byte data using the 443 * Base64 encoding scheme as specified in RFC 4648 and RFC 2045. 444 * 445 * <p> The Base64 padding character {@code '='} is accepted and 446 * interpreted as the end of the encoded byte data, but is not 447 * required. So if the final unit of the encoded byte data only has 448 * two or three Base64 characters (without the corresponding padding 449 * character(s) padded), they are decoded as if followed by padding 450 * character(s). If there is a padding character present in the 451 * final unit, the correct number of padding character(s) must be 452 * present, otherwise {@code IllegalArgumentException} ( 453 * {@code IOException} when reading from a Base64 stream) is thrown 454 * during decoding. 455 * 456 * <p> Instances of {@link Decoder} class are safe for use by 457 * multiple concurrent threads. 458 * 459 * <p> Unless otherwise noted, passing a {@code null} argument to 460 * a method of this class will cause a 461 * {@link java.lang.NullPointerException NullPointerException} to 462 * be thrown. 463 * 464 * @see Encoder 465 * @since 1.8 466 */ 467 public static class Decoder { 468 469 private final boolean isURL; 470 private final boolean isMIME; 471 472 private Decoder(boolean isURL, boolean isMIME) { 473 this.isURL = isURL; 474 this.isMIME = isMIME; 574 * if {@code src} is not in valid Base64 scheme, or {@code dst} 575 * does not have enough space for decoding all input bytes. 576 */ 577 public int decode(byte[] src, byte[] dst) { 578 int len = outLength(src, 0, src.length); 579 if (dst.length < len) 580 throw new IllegalArgumentException( 581 "Output byte array is too small for decoding all input bytes"); 582 return decode0(src, 0, src.length, dst); 583 } 584 585 /** 586 * Decodes all bytes from the input byte buffer using the {@link Base64} 587 * encoding scheme, writing the results into a newly-allocated ByteBuffer. 588 * 589 * <p> Upon return, the source buffer's position will be updated to 590 * its limit; its limit will not have been changed. The returned 591 * output buffer's position will be zero and its limit will be the 592 * number of resulting decoded bytes 593 * 594 * <p> {@code IllegalArgumentException} is thrown if the input buffer 595 * is not in valid Base64 encoding scheme. The position of the input 596 * buffer will not be advanced in this case. 597 * 598 * @param buffer 599 * the ByteBuffer to decode 600 * 601 * @return A newly-allocated byte buffer containing the decoded bytes 602 * 603 * @throws IllegalArgumentException 604 * if {@code src} is not in valid Base64 scheme. 605 */ 606 public ByteBuffer decode(ByteBuffer buffer) { 607 int pos0 = buffer.position(); 608 try { 609 byte[] src; 610 int sp, sl; 611 if (buffer.hasArray()) { 612 src = buffer.array(); 613 sp = buffer.arrayOffset() + buffer.position(); 614 sl = buffer.arrayOffset() + buffer.limit(); 615 buffer.position(buffer.limit()); 616 } else { 617 src = new byte[buffer.remaining()]; 618 buffer.get(src); 619 sp = 0; 620 sl = src.length; 621 } 622 byte[] dst = new byte[outLength(src, sp, sl)]; 623 return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst)); 624 } catch (IllegalArgumentException iae) { 625 buffer.position(pos0); 626 throw iae; 627 } 628 } 629 630 /** 631 * Returns an input stream for decoding {@link Base64} encoded byte stream. 632 * 633 * <p> The {@code read} methods of the returned {@code InputStream} will 634 * throw {@code IOException} when reading bytes that cannot be decoded. 635 * 636 * <p> Closing the returned input stream will close the underlying 637 * input stream. 638 * 639 * @param is 640 * the input stream 641 * 642 * @return the input stream for decoding the specified Base64 encoded 643 * byte stream 644 */ 645 public InputStream wrap(InputStream is) { 646 Objects.requireNonNull(is); 647 return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME); 648 } 649 650 private int outLength(byte[] src, int sp, int sl) { 651 int[] base64 = isURL ? fromBase64URL : fromBase64; 652 int paddings = 0; 653 int len = sl - sp; 654 if (len == 0) 655 return 0; 656 if (len < 2) { 657 if (isMIME && base64[0] == -1) 658 return 0; 659 throw new IllegalArgumentException( 660 "Input byte[] should at least have 2 bytes for base64 bytes"); 661 } 662 if (isMIME) { 663 // scan all bytes to fill out all non-alphabet. a performance 664 // trade-off of pre-scan or Arrays.copyOf 665 int n = 0; 666 while (sp < sl) { 667 int b = src[sp++] & 0xff; 668 if (b == '=') { 669 len -= (sl - sp + 1); 677 if (src[sl - 1] == '=') { 678 paddings++; 679 if (src[sl - 2] == '=') 680 paddings++; 681 } 682 } 683 if (paddings == 0 && (len & 0x3) != 0) 684 paddings = 4 - (len & 0x3); 685 return 3 * ((len + 3) / 4) - paddings; 686 } 687 688 private int decode0(byte[] src, int sp, int sl, byte[] dst) { 689 int[] base64 = isURL ? fromBase64URL : fromBase64; 690 int dp = 0; 691 int bits = 0; 692 int shiftto = 18; // pos of first byte of 4-byte atom 693 while (sp < sl) { 694 int b = src[sp++] & 0xff; 695 if ((b = base64[b]) < 0) { 696 if (b == -2) { // padding byte '=' 697 // = shiftto==18 unnecessary padding 698 // x= shiftto==12 a dangling single x 699 // x to be handled together with non-padding case 700 // xx= shiftto==6&&sp==sl missing last = 701 // xx=y shiftto==6 last is not = 702 if (shiftto == 6 && (sp == sl || src[sp++] != '=') || 703 shiftto == 18) { 704 throw new IllegalArgumentException( 705 "Input byte array has wrong 4-byte ending unit"); 706 } 707 break; 708 } 709 if (isMIME) // skip if for rfc2045 710 continue; 711 else 712 throw new IllegalArgumentException( 713 "Illegal base64 character " + 714 Integer.toString(src[sp - 1], 16)); 715 } 716 bits |= (b << shiftto); 717 shiftto -= 6; 718 if (shiftto < 0) { 719 dst[dp++] = (byte)(bits >> 16); 720 dst[dp++] = (byte)(bits >> 8); 721 dst[dp++] = (byte)(bits); 722 shiftto = 18; 723 bits = 0; 724 } 725 } 726 // reached end of byte array or hit padding '=' characters. 727 if (shiftto == 6) { 728 dst[dp++] = (byte)(bits >> 16); 729 } else if (shiftto == 0) { 730 dst[dp++] = (byte)(bits >> 16); 731 dst[dp++] = (byte)(bits >> 8); 732 } else if (shiftto == 12) { 733 // dangling single "x", incorrectly encoded. 734 throw new IllegalArgumentException( 735 "Last unit does not have enough valid bits"); 736 } 737 // anything left is invalid, if is not MIME. 738 // if MIME, ignore all non-base64 character 739 while (sp < sl) { 740 if (isMIME && base64[src[sp++]] < 0) 741 continue; 742 throw new IllegalArgumentException( 743 "Input byte array has incorrect ending byte at " + sp); 744 } 745 return dp; 746 } 747 } 748 749 /* 750 * An output stream for encoding bytes into the Base64. 751 */ 752 private static class EncOutputStream extends FilterOutputStream { 753 754 private int leftover = 0; 755 private int b0, b1, b2; 756 private boolean closed = false; 757 758 private final char[] base64; // byte->base64 mapping 759 private final byte[] newline; // line separator, if needed 760 private final int linemax; 761 private final boolean doPadding;// whether or not to pad 921 if (len == 0) { // no enough output space 922 bits >>= 8; // shift to lowest byte 923 nextout = 0; 924 } else { 925 b[off++] = (byte) (bits >> 8); 926 } 927 } 928 } 929 if (off == oldOff) 930 return -1; 931 else 932 return off - oldOff; 933 } 934 if (v == '=') { // padding byte(s) 935 // = shiftto==18 unnecessary padding 936 // x= shiftto==12 dangling x, invalid unit 937 // xx= shiftto==6 && missing last '=' 938 // xx=y or last is not '=' 939 if (nextin == 18 || nextin == 12 || 940 nextin == 6 && is.read() != '=') { 941 throw new IOException("Illegal base64 ending sequence:" + nextin); 942 } 943 b[off++] = (byte)(bits >> (16)); 944 len--; 945 if (nextin == 0) { // only one padding byte 946 if (len == 0) { // no enough output space 947 bits >>= 8; // shift to lowest byte 948 nextout = 0; 949 } else { 950 b[off++] = (byte) (bits >> 8); 951 } 952 } 953 eof = true; 954 break; 955 } 956 if ((v = base64[v]) == -1) { 957 if (isMIME) // skip if for rfc2045 958 continue; 959 else 960 throw new IOException("Illegal base64 character " + 961 Integer.toString(v, 16)); 962 } 963 bits |= (v << nextin); 964 if (nextin == 0) { 965 nextin = 18; // clear for next 966 nextout = 16; 967 while (nextout >= 0) { 968 b[off++] = (byte)(bits >> nextout); 969 len--; 970 nextout -= 8; 971 if (len == 0 && nextout >= 0) { // don't clean "bits" 972 return off - oldOff; |