Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/java/util/Base64.java
41152 views
1
/*
2
* Copyright (c) 2012, 2020, 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
34
import sun.nio.cs.ISO_8859_1;
35
36
import jdk.internal.vm.annotation.IntrinsicCandidate;
37
38
/**
39
* This class consists exclusively of static methods for obtaining
40
* encoders and decoders for the Base64 encoding scheme. The
41
* implementation of this class supports the following types of Base64
42
* as specified in
43
* <a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a> and
44
* <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>.
45
*
46
* <ul>
47
* <li><a id="basic"><b>Basic</b></a>
48
* <p> Uses "The Base64 Alphabet" as specified in Table 1 of
49
* RFC 4648 and RFC 2045 for encoding and decoding operation.
50
* The encoder does not add any line feed (line separator)
51
* character. The decoder rejects data that contains characters
52
* outside the base64 alphabet.</p></li>
53
*
54
* <li><a id="url"><b>URL and Filename safe</b></a>
55
* <p> Uses the "URL and Filename safe Base64 Alphabet" as specified
56
* in Table 2 of RFC 4648 for encoding and decoding. The
57
* encoder does not add any line feed (line separator) character.
58
* The decoder rejects data that contains characters outside the
59
* base64 alphabet.</p></li>
60
*
61
* <li><a id="mime"><b>MIME</b></a>
62
* <p> Uses "The Base64 Alphabet" as specified in Table 1 of
63
* RFC 2045 for encoding and decoding operation. The encoded output
64
* must be represented in lines of no more than 76 characters each
65
* and uses a carriage return {@code '\r'} followed immediately by
66
* a linefeed {@code '\n'} as the line separator. No line separator
67
* is added to the end of the encoded output. All line separators
68
* or other characters not found in the base64 alphabet table are
69
* ignored in decoding operation.</p></li>
70
* </ul>
71
*
72
* <p> Unless otherwise noted, passing a {@code null} argument to a
73
* method of this class will cause a {@link java.lang.NullPointerException
74
* NullPointerException} to be thrown.
75
*
76
* @author Xueming Shen
77
* @since 1.8
78
*/
79
80
public class Base64 {
81
82
private Base64() {}
83
84
/**
85
* Returns a {@link Encoder} that encodes using the
86
* <a href="#basic">Basic</a> type base64 encoding scheme.
87
*
88
* @return A Base64 encoder.
89
*/
90
public static Encoder getEncoder() {
91
return Encoder.RFC4648;
92
}
93
94
/**
95
* Returns a {@link Encoder} that encodes using the
96
* <a href="#url">URL and Filename safe</a> type base64
97
* encoding scheme.
98
*
99
* @return A Base64 encoder.
100
*/
101
public static Encoder getUrlEncoder() {
102
return Encoder.RFC4648_URLSAFE;
103
}
104
105
/**
106
* Returns a {@link Encoder} that encodes using the
107
* <a href="#mime">MIME</a> type base64 encoding scheme.
108
*
109
* @return A Base64 encoder.
110
*/
111
public static Encoder getMimeEncoder() {
112
return Encoder.RFC2045;
113
}
114
115
/**
116
* Returns a {@link Encoder} that encodes using the
117
* <a href="#mime">MIME</a> type base64 encoding scheme
118
* with specified line length and line separators.
119
*
120
* @param lineLength
121
* the length of each output line (rounded down to nearest multiple
122
* of 4). If the rounded down line length is not a positive value,
123
* the output will not be separated in lines
124
* @param lineSeparator
125
* the line separator for each output line
126
*
127
* @return A Base64 encoder.
128
*
129
* @throws IllegalArgumentException if {@code lineSeparator} includes any
130
* character of "The Base64 Alphabet" as specified in Table 1 of
131
* RFC 2045.
132
*/
133
public static Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) {
134
Objects.requireNonNull(lineSeparator);
135
int[] base64 = Decoder.fromBase64;
136
for (byte b : lineSeparator) {
137
if (base64[b & 0xff] != -1)
138
throw new IllegalArgumentException(
139
"Illegal base64 line separator character 0x" + Integer.toString(b, 16));
140
}
141
// round down to nearest multiple of 4
142
lineLength &= ~0b11;
143
if (lineLength <= 0) {
144
return Encoder.RFC4648;
145
}
146
return new Encoder(false, lineSeparator, lineLength, true);
147
}
148
149
/**
150
* Returns a {@link Decoder} that decodes using the
151
* <a href="#basic">Basic</a> type base64 encoding scheme.
152
*
153
* @return A Base64 decoder.
154
*/
155
public static Decoder getDecoder() {
156
return Decoder.RFC4648;
157
}
158
159
/**
160
* Returns a {@link Decoder} that decodes using the
161
* <a href="#url">URL and Filename safe</a> type base64
162
* encoding scheme.
163
*
164
* @return A Base64 decoder.
165
*/
166
public static Decoder getUrlDecoder() {
167
return Decoder.RFC4648_URLSAFE;
168
}
169
170
/**
171
* Returns a {@link Decoder} that decodes using the
172
* <a href="#mime">MIME</a> type base64 decoding scheme.
173
*
174
* @return A Base64 decoder.
175
*/
176
public static Decoder getMimeDecoder() {
177
return Decoder.RFC2045;
178
}
179
180
/**
181
* This class implements an encoder for encoding byte data using
182
* the Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
183
*
184
* <p> Instances of {@link Encoder} class are safe for use by
185
* multiple concurrent threads.
186
*
187
* <p> Unless otherwise noted, passing a {@code null} argument to
188
* a method of this class will cause a
189
* {@link java.lang.NullPointerException NullPointerException} to
190
* be thrown.
191
* <p> If the encoded byte output of the needed size can not
192
* be allocated, the encode methods of this class will
193
* cause an {@link java.lang.OutOfMemoryError OutOfMemoryError}
194
* to be thrown.
195
*
196
* @see Decoder
197
* @since 1.8
198
*/
199
public static class Encoder {
200
201
private final byte[] newline;
202
private final int linemax;
203
private final boolean isURL;
204
private final boolean doPadding;
205
206
private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) {
207
this.isURL = isURL;
208
this.newline = newline;
209
this.linemax = linemax;
210
this.doPadding = doPadding;
211
}
212
213
/**
214
* This array is a lookup table that translates 6-bit positive integer
215
* index values into their "Base64 Alphabet" equivalents as specified
216
* in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648).
217
*/
218
private static final char[] toBase64 = {
219
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
220
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
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
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
224
};
225
226
/**
227
* It's the lookup table for "URL and Filename safe Base64" as specified
228
* in Table 2 of the RFC 4648, with the '+' and '/' changed to '-' and
229
* '_'. This table is used when BASE64_URL is specified.
230
*/
231
private static final char[] toBase64URL = {
232
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
233
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
234
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
235
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
236
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
237
};
238
239
private static final int MIMELINEMAX = 76;
240
private static final byte[] CRLF = new byte[] {'\r', '\n'};
241
242
static final Encoder RFC4648 = new Encoder(false, null, -1, true);
243
static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
244
static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
245
246
/**
247
* Calculates the length of the encoded output bytes.
248
*
249
* @param srclen length of the bytes to encode
250
* @param throwOOME if true, throws OutOfMemoryError if the length of
251
* the encoded bytes overflows; else returns the
252
* length
253
* @return length of the encoded bytes, or -1 if the length overflows
254
*
255
*/
256
private final int encodedOutLength(int srclen, boolean throwOOME) {
257
int len = 0;
258
try {
259
if (doPadding) {
260
len = Math.multiplyExact(4, (Math.addExact(srclen, 2) / 3));
261
} else {
262
int n = srclen % 3;
263
len = Math.addExact(Math.multiplyExact(4, (srclen / 3)), (n == 0 ? 0 : n + 1));
264
}
265
if (linemax > 0) { // line separators
266
len = Math.addExact(len, (len - 1) / linemax * newline.length);
267
}
268
} catch (ArithmeticException ex) {
269
if (throwOOME) {
270
throw new OutOfMemoryError("Encoded size is too large");
271
} else {
272
// let the caller know that encoded bytes length
273
// is too large
274
len = -1;
275
}
276
}
277
return len;
278
}
279
280
/**
281
* Encodes all bytes from the specified byte array into a newly-allocated
282
* byte array using the {@link Base64} encoding scheme. The returned byte
283
* array is of the length of the resulting bytes.
284
*
285
* @param src
286
* the byte array to encode
287
* @return A newly-allocated byte array containing the resulting
288
* encoded bytes.
289
*/
290
public byte[] encode(byte[] src) {
291
int len = encodedOutLength(src.length, true); // dst array size
292
byte[] dst = new byte[len];
293
int ret = encode0(src, 0, src.length, dst);
294
if (ret != dst.length)
295
return Arrays.copyOf(dst, ret);
296
return dst;
297
}
298
299
/**
300
* Encodes all bytes from the specified byte array using the
301
* {@link Base64} encoding scheme, writing the resulting bytes to the
302
* given output byte array, starting at offset 0.
303
*
304
* <p> It is the responsibility of the invoker of this method to make
305
* sure the output byte array {@code dst} has enough space for encoding
306
* all bytes from the input byte array. No bytes will be written to the
307
* output byte array if the output byte array is not big enough.
308
*
309
* @param src
310
* the byte array to encode
311
* @param dst
312
* the output byte array
313
* @return The number of bytes written to the output byte array
314
*
315
* @throws IllegalArgumentException if {@code dst} does not have enough
316
* space for encoding all input bytes.
317
*/
318
public int encode(byte[] src, byte[] dst) {
319
int len = encodedOutLength(src.length, false); // dst array size
320
if (dst.length < len || len == -1)
321
throw new IllegalArgumentException(
322
"Output byte array is too small for encoding all input bytes");
323
return encode0(src, 0, src.length, dst);
324
}
325
326
/**
327
* Encodes the specified byte array into a String using the {@link Base64}
328
* encoding scheme.
329
*
330
* <p> This method first encodes all input bytes into a base64 encoded
331
* byte array and then constructs a new String by using the encoded byte
332
* array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1
333
* ISO-8859-1} charset.
334
*
335
* <p> In other words, an invocation of this method has exactly the same
336
* effect as invoking
337
* {@code new String(encode(src), StandardCharsets.ISO_8859_1)}.
338
*
339
* @param src
340
* the byte array to encode
341
* @return A String containing the resulting Base64 encoded characters
342
*/
343
@SuppressWarnings("deprecation")
344
public String encodeToString(byte[] src) {
345
byte[] encoded = encode(src);
346
return new String(encoded, 0, 0, encoded.length);
347
}
348
349
/**
350
* Encodes all remaining bytes from the specified byte buffer into
351
* a newly-allocated ByteBuffer using the {@link Base64} encoding
352
* scheme.
353
*
354
* Upon return, the source buffer's position will be updated to
355
* its limit; its limit will not have been changed. The returned
356
* output buffer's position will be zero and its limit will be the
357
* number of resulting encoded bytes.
358
*
359
* @param buffer
360
* the source ByteBuffer to encode
361
* @return A newly-allocated byte buffer containing the encoded bytes.
362
*/
363
public ByteBuffer encode(ByteBuffer buffer) {
364
int len = encodedOutLength(buffer.remaining(), true);
365
byte[] dst = new byte[len];
366
int ret = 0;
367
if (buffer.hasArray()) {
368
ret = encode0(buffer.array(),
369
buffer.arrayOffset() + buffer.position(),
370
buffer.arrayOffset() + buffer.limit(),
371
dst);
372
buffer.position(buffer.limit());
373
} else {
374
byte[] src = new byte[buffer.remaining()];
375
buffer.get(src);
376
ret = encode0(src, 0, src.length, dst);
377
}
378
if (ret != dst.length)
379
dst = Arrays.copyOf(dst, ret);
380
return ByteBuffer.wrap(dst);
381
}
382
383
/**
384
* Wraps an output stream for encoding byte data using the {@link Base64}
385
* encoding scheme.
386
*
387
* <p> It is recommended to promptly close the returned output stream after
388
* use, during which it will flush all possible leftover bytes to the underlying
389
* output stream. Closing the returned output stream will close the underlying
390
* output stream.
391
*
392
* @param os
393
* the output stream.
394
* @return the output stream for encoding the byte data into the
395
* specified Base64 encoded format
396
*/
397
public OutputStream wrap(OutputStream os) {
398
Objects.requireNonNull(os);
399
return new EncOutputStream(os, isURL ? toBase64URL : toBase64,
400
newline, linemax, doPadding);
401
}
402
403
/**
404
* Returns an encoder instance that encodes equivalently to this one,
405
* but without adding any padding character at the end of the encoded
406
* byte data.
407
*
408
* <p> The encoding scheme of this encoder instance is unaffected by
409
* this invocation. The returned encoder instance should be used for
410
* non-padding encoding operation.
411
*
412
* @return an equivalent encoder that encodes without adding any
413
* padding character at the end
414
*/
415
public Encoder withoutPadding() {
416
if (!doPadding)
417
return this;
418
return new Encoder(isURL, newline, linemax, false);
419
}
420
421
@IntrinsicCandidate
422
private void encodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL) {
423
char[] base64 = isURL ? toBase64URL : toBase64;
424
for (int sp0 = sp, dp0 = dp ; sp0 < sl; ) {
425
int bits = (src[sp0++] & 0xff) << 16 |
426
(src[sp0++] & 0xff) << 8 |
427
(src[sp0++] & 0xff);
428
dst[dp0++] = (byte)base64[(bits >>> 18) & 0x3f];
429
dst[dp0++] = (byte)base64[(bits >>> 12) & 0x3f];
430
dst[dp0++] = (byte)base64[(bits >>> 6) & 0x3f];
431
dst[dp0++] = (byte)base64[bits & 0x3f];
432
}
433
}
434
435
private int encode0(byte[] src, int off, int end, byte[] dst) {
436
char[] base64 = isURL ? toBase64URL : toBase64;
437
int sp = off;
438
int slen = (end - off) / 3 * 3;
439
int sl = off + slen;
440
if (linemax > 0 && slen > linemax / 4 * 3)
441
slen = linemax / 4 * 3;
442
int dp = 0;
443
while (sp < sl) {
444
int sl0 = Math.min(sp + slen, sl);
445
encodeBlock(src, sp, sl0, dst, dp, isURL);
446
int dlen = (sl0 - sp) / 3 * 4;
447
dp += dlen;
448
sp = sl0;
449
if (dlen == linemax && sp < end) {
450
for (byte b : newline){
451
dst[dp++] = b;
452
}
453
}
454
}
455
if (sp < end) { // 1 or 2 leftover bytes
456
int b0 = src[sp++] & 0xff;
457
dst[dp++] = (byte)base64[b0 >> 2];
458
if (sp == end) {
459
dst[dp++] = (byte)base64[(b0 << 4) & 0x3f];
460
if (doPadding) {
461
dst[dp++] = '=';
462
dst[dp++] = '=';
463
}
464
} else {
465
int b1 = src[sp++] & 0xff;
466
dst[dp++] = (byte)base64[(b0 << 4) & 0x3f | (b1 >> 4)];
467
dst[dp++] = (byte)base64[(b1 << 2) & 0x3f];
468
if (doPadding) {
469
dst[dp++] = '=';
470
}
471
}
472
}
473
return dp;
474
}
475
}
476
477
/**
478
* This class implements a decoder for decoding byte data using the
479
* Base64 encoding scheme as specified in RFC 4648 and RFC 2045.
480
*
481
* <p> The Base64 padding character {@code '='} is accepted and
482
* interpreted as the end of the encoded byte data, but is not
483
* required. So if the final unit of the encoded byte data only has
484
* two or three Base64 characters (without the corresponding padding
485
* character(s) padded), they are decoded as if followed by padding
486
* character(s). If there is a padding character present in the
487
* final unit, the correct number of padding character(s) must be
488
* present, otherwise {@code IllegalArgumentException} (
489
* {@code IOException} when reading from a Base64 stream) is thrown
490
* during decoding.
491
*
492
* <p> Instances of {@link Decoder} class are safe for use by
493
* multiple concurrent threads.
494
*
495
* <p> Unless otherwise noted, passing a {@code null} argument to
496
* a method of this class will cause a
497
* {@link java.lang.NullPointerException NullPointerException} to
498
* be thrown.
499
* <p> If the decoded byte output of the needed size can not
500
* be allocated, the decode methods of this class will
501
* cause an {@link java.lang.OutOfMemoryError OutOfMemoryError}
502
* to be thrown.
503
*
504
* @see Encoder
505
* @since 1.8
506
*/
507
public static class Decoder {
508
509
private final boolean isURL;
510
private final boolean isMIME;
511
512
private Decoder(boolean isURL, boolean isMIME) {
513
this.isURL = isURL;
514
this.isMIME = isMIME;
515
}
516
517
/**
518
* Lookup table for decoding unicode characters drawn from the
519
* "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into
520
* their 6-bit positive integer equivalents. Characters that
521
* are not in the Base64 alphabet but fall within the bounds of
522
* the array are encoded to -1.
523
*
524
*/
525
private static final int[] fromBase64 = new int[256];
526
static {
527
Arrays.fill(fromBase64, -1);
528
for (int i = 0; i < Encoder.toBase64.length; i++)
529
fromBase64[Encoder.toBase64[i]] = i;
530
fromBase64['='] = -2;
531
}
532
533
/**
534
* Lookup table for decoding "URL and Filename safe Base64 Alphabet"
535
* as specified in Table2 of the RFC 4648.
536
*/
537
private static final int[] fromBase64URL = new int[256];
538
539
static {
540
Arrays.fill(fromBase64URL, -1);
541
for (int i = 0; i < Encoder.toBase64URL.length; i++)
542
fromBase64URL[Encoder.toBase64URL[i]] = i;
543
fromBase64URL['='] = -2;
544
}
545
546
static final Decoder RFC4648 = new Decoder(false, false);
547
static final Decoder RFC4648_URLSAFE = new Decoder(true, false);
548
static final Decoder RFC2045 = new Decoder(false, true);
549
550
/**
551
* Decodes all bytes from the input byte array using the {@link Base64}
552
* encoding scheme, writing the results into a newly-allocated output
553
* byte array. The returned byte array is of the length of the resulting
554
* bytes.
555
*
556
* @param src
557
* the byte array to decode
558
*
559
* @return A newly-allocated byte array containing the decoded bytes.
560
*
561
* @throws IllegalArgumentException
562
* if {@code src} is not in valid Base64 scheme
563
*/
564
public byte[] decode(byte[] src) {
565
byte[] dst = new byte[decodedOutLength(src, 0, src.length)];
566
int ret = decode0(src, 0, src.length, dst);
567
if (ret != dst.length) {
568
dst = Arrays.copyOf(dst, ret);
569
}
570
return dst;
571
}
572
573
/**
574
* Decodes a Base64 encoded String into a newly-allocated byte array
575
* using the {@link Base64} encoding scheme.
576
*
577
* <p> An invocation of this method has exactly the same effect as invoking
578
* {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))}
579
*
580
* @param src
581
* the string to decode
582
*
583
* @return A newly-allocated byte array containing the decoded bytes.
584
*
585
* @throws IllegalArgumentException
586
* if {@code src} is not in valid Base64 scheme
587
*/
588
public byte[] decode(String src) {
589
return decode(src.getBytes(ISO_8859_1.INSTANCE));
590
}
591
592
/**
593
* Decodes all bytes from the input byte array using the {@link Base64}
594
* encoding scheme, writing the results into the given output byte array,
595
* starting at offset 0.
596
*
597
* <p> It is the responsibility of the invoker of this method to make
598
* sure the output byte array {@code dst} has enough space for decoding
599
* all bytes from the input byte array. No bytes will be written to
600
* the output byte array if the output byte array is not big enough.
601
*
602
* <p> If the input byte array is not in valid Base64 encoding scheme
603
* then some bytes may have been written to the output byte array before
604
* IllegalargumentException is thrown.
605
*
606
* @param src
607
* the byte array to decode
608
* @param dst
609
* the output byte array
610
*
611
* @return The number of bytes written to the output byte array
612
*
613
* @throws IllegalArgumentException
614
* if {@code src} is not in valid Base64 scheme, or {@code dst}
615
* does not have enough space for decoding all input bytes.
616
*/
617
public int decode(byte[] src, byte[] dst) {
618
int len = decodedOutLength(src, 0, src.length);
619
if (dst.length < len || len == -1)
620
throw new IllegalArgumentException(
621
"Output byte array is too small for decoding all input bytes");
622
return decode0(src, 0, src.length, dst);
623
}
624
625
/**
626
* Decodes all bytes from the input byte buffer using the {@link Base64}
627
* encoding scheme, writing the results into a newly-allocated ByteBuffer.
628
*
629
* <p> Upon return, the source buffer's position will be updated to
630
* its limit; its limit will not have been changed. The returned
631
* output buffer's position will be zero and its limit will be the
632
* number of resulting decoded bytes
633
*
634
* <p> {@code IllegalArgumentException} is thrown if the input buffer
635
* is not in valid Base64 encoding scheme. The position of the input
636
* buffer will not be advanced in this case.
637
*
638
* @param buffer
639
* the ByteBuffer to decode
640
*
641
* @return A newly-allocated byte buffer containing the decoded bytes
642
*
643
* @throws IllegalArgumentException
644
* if {@code buffer} is not in valid Base64 scheme
645
*/
646
public ByteBuffer decode(ByteBuffer buffer) {
647
int pos0 = buffer.position();
648
try {
649
byte[] src;
650
int sp, sl;
651
if (buffer.hasArray()) {
652
src = buffer.array();
653
sp = buffer.arrayOffset() + buffer.position();
654
sl = buffer.arrayOffset() + buffer.limit();
655
buffer.position(buffer.limit());
656
} else {
657
src = new byte[buffer.remaining()];
658
buffer.get(src);
659
sp = 0;
660
sl = src.length;
661
}
662
byte[] dst = new byte[decodedOutLength(src, sp, sl)];
663
return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst));
664
} catch (IllegalArgumentException iae) {
665
buffer.position(pos0);
666
throw iae;
667
}
668
}
669
670
/**
671
* Returns an input stream for decoding {@link Base64} encoded byte stream.
672
*
673
* <p> The {@code read} methods of the returned {@code InputStream} will
674
* throw {@code IOException} when reading bytes that cannot be decoded.
675
*
676
* <p> Closing the returned input stream will close the underlying
677
* input stream.
678
*
679
* @param is
680
* the input stream
681
*
682
* @return the input stream for decoding the specified Base64 encoded
683
* byte stream
684
*/
685
public InputStream wrap(InputStream is) {
686
Objects.requireNonNull(is);
687
return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME);
688
}
689
690
/**
691
* Calculates the length of the decoded output bytes.
692
*
693
* @param src the byte array to decode
694
* @param sp the source position
695
* @param sl the source limit
696
*
697
* @return length of the decoded bytes
698
*
699
*/
700
private int decodedOutLength(byte[] src, int sp, int sl) {
701
int[] base64 = isURL ? fromBase64URL : fromBase64;
702
int paddings = 0;
703
int len = sl - sp;
704
if (len == 0)
705
return 0;
706
if (len < 2) {
707
if (isMIME && base64[0] == -1)
708
return 0;
709
throw new IllegalArgumentException(
710
"Input byte[] should at least have 2 bytes for base64 bytes");
711
}
712
if (isMIME) {
713
// scan all bytes to fill out all non-alphabet. a performance
714
// trade-off of pre-scan or Arrays.copyOf
715
int n = 0;
716
while (sp < sl) {
717
int b = src[sp++] & 0xff;
718
if (b == '=') {
719
len -= (sl - sp + 1);
720
break;
721
}
722
if ((b = base64[b]) == -1)
723
n++;
724
}
725
len -= n;
726
} else {
727
if (src[sl - 1] == '=') {
728
paddings++;
729
if (src[sl - 2] == '=')
730
paddings++;
731
}
732
}
733
if (paddings == 0 && (len & 0x3) != 0)
734
paddings = 4 - (len & 0x3);
735
736
// If len is near to Integer.MAX_VALUE, (len + 3)
737
// can possibly overflow, perform this operation as
738
// long and cast it back to integer when the value comes under
739
// integer limit. The final value will always be in integer
740
// limits
741
return 3 * (int) ((len + 3L) / 4) - paddings;
742
}
743
744
/**
745
* Decodes base64 characters, and returns the number of data bytes
746
* written into the destination array.
747
*
748
* It is the fast path for full 4-byte to 3-byte decoding w/o errors.
749
*
750
* decodeBlock() can be overridden by an arch-specific intrinsic.
751
* decodeBlock can choose to decode all, none, or a variable-sized
752
* prefix of the src bytes. This allows the intrinsic to decode in
753
* chunks of the src that are of a favorable size for the specific
754
* processor it's running on.
755
*
756
* If the intrinsic function does not process all of the bytes in
757
* src, it must process a multiple of four of them, making the
758
* returned destination length a multiple of three.
759
*
760
* If any illegal base64 bytes are encountered in src by the
761
* intrinsic, the intrinsic must return the actual number of valid
762
* data bytes already written to dst. Note that the '=' pad
763
* character is treated as an illegal Base64 character by
764
* decodeBlock, so it will not process a block of 4 bytes
765
* containing pad characters.
766
*
767
* Given the parameters, no length check is possible on dst, so dst
768
* is assumed to be large enough to store the decoded bytes.
769
*
770
* @param src
771
* the source byte array of Base64 encoded bytes
772
* @param sp
773
* the offset into src array to begin reading
774
* @param sl
775
* the offset (exclusive) past the last byte to be converted.
776
* @param dst
777
* the destination byte array of decoded data bytes
778
* @param dp
779
* the offset into dst array to begin writing
780
* @param isURL
781
* boolean, when true decode RFC4648 URL-safe base64 characters
782
* @return the number of destination data bytes produced
783
*/
784
@IntrinsicCandidate
785
private int decodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL) {
786
int[] base64 = isURL ? fromBase64URL : fromBase64;
787
int sl0 = sp + ((sl - sp) & ~0b11);
788
int new_dp = dp;
789
while (sp < sl0) {
790
int b1 = base64[src[sp++] & 0xff];
791
int b2 = base64[src[sp++] & 0xff];
792
int b3 = base64[src[sp++] & 0xff];
793
int b4 = base64[src[sp++] & 0xff];
794
if ((b1 | b2 | b3 | b4) < 0) { // non base64 byte
795
return new_dp - dp;
796
}
797
int bits0 = b1 << 18 | b2 << 12 | b3 << 6 | b4;
798
dst[new_dp++] = (byte)(bits0 >> 16);
799
dst[new_dp++] = (byte)(bits0 >> 8);
800
dst[new_dp++] = (byte)(bits0);
801
}
802
return new_dp - dp;
803
}
804
805
private int decode0(byte[] src, int sp, int sl, byte[] dst) {
806
int[] base64 = isURL ? fromBase64URL : fromBase64;
807
int dp = 0;
808
int bits = 0;
809
int shiftto = 18; // pos of first byte of 4-byte atom
810
811
while (sp < sl) {
812
if (shiftto == 18 && sp < sl - 4) { // fast path
813
int dl = decodeBlock(src, sp, sl, dst, dp, isURL);
814
/*
815
* Calculate how many characters were processed by how many
816
* bytes of data were returned.
817
*/
818
int chars_decoded = (dl / 3) * 4;
819
820
sp += chars_decoded;
821
dp += dl;
822
}
823
if (sp >= sl) {
824
// we're done
825
break;
826
}
827
int b = src[sp++] & 0xff;
828
if ((b = base64[b]) < 0) {
829
if (b == -2) { // padding byte '='
830
// = shiftto==18 unnecessary padding
831
// x= shiftto==12 a dangling single x
832
// x to be handled together with non-padding case
833
// xx= shiftto==6&&sp==sl missing last =
834
// xx=y shiftto==6 last is not =
835
if (shiftto == 6 && (sp == sl || src[sp++] != '=') ||
836
shiftto == 18) {
837
throw new IllegalArgumentException(
838
"Input byte array has wrong 4-byte ending unit");
839
}
840
break;
841
}
842
if (isMIME) // skip if for rfc2045
843
continue;
844
else
845
throw new IllegalArgumentException(
846
"Illegal base64 character " +
847
Integer.toString(src[sp - 1], 16));
848
}
849
bits |= (b << shiftto);
850
shiftto -= 6;
851
if (shiftto < 0) {
852
dst[dp++] = (byte)(bits >> 16);
853
dst[dp++] = (byte)(bits >> 8);
854
dst[dp++] = (byte)(bits);
855
shiftto = 18;
856
bits = 0;
857
}
858
}
859
// reached end of byte array or hit padding '=' characters.
860
if (shiftto == 6) {
861
dst[dp++] = (byte)(bits >> 16);
862
} else if (shiftto == 0) {
863
dst[dp++] = (byte)(bits >> 16);
864
dst[dp++] = (byte)(bits >> 8);
865
} else if (shiftto == 12) {
866
// dangling single "x", incorrectly encoded.
867
throw new IllegalArgumentException(
868
"Last unit does not have enough valid bits");
869
}
870
// anything left is invalid, if is not MIME.
871
// if MIME, ignore all non-base64 character
872
while (sp < sl) {
873
if (isMIME && base64[src[sp++] & 0xff] < 0)
874
continue;
875
throw new IllegalArgumentException(
876
"Input byte array has incorrect ending byte at " + sp);
877
}
878
return dp;
879
}
880
}
881
882
/*
883
* An output stream for encoding bytes into the Base64.
884
*/
885
private static class EncOutputStream extends FilterOutputStream {
886
887
private int leftover = 0;
888
private int b0, b1, b2;
889
private boolean closed = false;
890
891
private final char[] base64; // byte->base64 mapping
892
private final byte[] newline; // line separator, if needed
893
private final int linemax;
894
private final boolean doPadding;// whether or not to pad
895
private int linepos = 0;
896
private byte[] buf;
897
898
EncOutputStream(OutputStream os, char[] base64,
899
byte[] newline, int linemax, boolean doPadding) {
900
super(os);
901
this.base64 = base64;
902
this.newline = newline;
903
this.linemax = linemax;
904
this.doPadding = doPadding;
905
this.buf = new byte[linemax <= 0 ? 8124 : linemax];
906
}
907
908
@Override
909
public void write(int b) throws IOException {
910
byte[] buf = new byte[1];
911
buf[0] = (byte)(b & 0xff);
912
write(buf, 0, 1);
913
}
914
915
private void checkNewline() throws IOException {
916
if (linepos == linemax) {
917
out.write(newline);
918
linepos = 0;
919
}
920
}
921
922
private void writeb4(char b1, char b2, char b3, char b4) throws IOException {
923
buf[0] = (byte)b1;
924
buf[1] = (byte)b2;
925
buf[2] = (byte)b3;
926
buf[3] = (byte)b4;
927
out.write(buf, 0, 4);
928
}
929
930
@Override
931
public void write(byte[] b, int off, int len) throws IOException {
932
if (closed)
933
throw new IOException("Stream is closed");
934
if (off < 0 || len < 0 || len > b.length - off)
935
throw new ArrayIndexOutOfBoundsException();
936
if (len == 0)
937
return;
938
if (leftover != 0) {
939
if (leftover == 1) {
940
b1 = b[off++] & 0xff;
941
len--;
942
if (len == 0) {
943
leftover++;
944
return;
945
}
946
}
947
b2 = b[off++] & 0xff;
948
len--;
949
checkNewline();
950
writeb4(base64[b0 >> 2],
951
base64[(b0 << 4) & 0x3f | (b1 >> 4)],
952
base64[(b1 << 2) & 0x3f | (b2 >> 6)],
953
base64[b2 & 0x3f]);
954
linepos += 4;
955
}
956
int nBits24 = len / 3;
957
leftover = len - (nBits24 * 3);
958
959
while (nBits24 > 0) {
960
checkNewline();
961
int dl = linemax <= 0 ? buf.length : buf.length - linepos;
962
int sl = off + Math.min(nBits24, dl / 4) * 3;
963
int dp = 0;
964
for (int sp = off; sp < sl; ) {
965
int bits = (b[sp++] & 0xff) << 16 |
966
(b[sp++] & 0xff) << 8 |
967
(b[sp++] & 0xff);
968
buf[dp++] = (byte)base64[(bits >>> 18) & 0x3f];
969
buf[dp++] = (byte)base64[(bits >>> 12) & 0x3f];
970
buf[dp++] = (byte)base64[(bits >>> 6) & 0x3f];
971
buf[dp++] = (byte)base64[bits & 0x3f];
972
}
973
out.write(buf, 0, dp);
974
off = sl;
975
linepos += dp;
976
nBits24 -= dp / 4;
977
}
978
if (leftover == 1) {
979
b0 = b[off++] & 0xff;
980
} else if (leftover == 2) {
981
b0 = b[off++] & 0xff;
982
b1 = b[off++] & 0xff;
983
}
984
}
985
986
@Override
987
public void close() throws IOException {
988
if (!closed) {
989
closed = true;
990
if (leftover == 1) {
991
checkNewline();
992
out.write(base64[b0 >> 2]);
993
out.write(base64[(b0 << 4) & 0x3f]);
994
if (doPadding) {
995
out.write('=');
996
out.write('=');
997
}
998
} else if (leftover == 2) {
999
checkNewline();
1000
out.write(base64[b0 >> 2]);
1001
out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]);
1002
out.write(base64[(b1 << 2) & 0x3f]);
1003
if (doPadding) {
1004
out.write('=');
1005
}
1006
}
1007
leftover = 0;
1008
out.close();
1009
}
1010
}
1011
}
1012
1013
/*
1014
* An input stream for decoding Base64 bytes
1015
*/
1016
private static class DecInputStream extends InputStream {
1017
1018
private final InputStream is;
1019
private final boolean isMIME;
1020
private final int[] base64; // base64 -> byte mapping
1021
private int bits = 0; // 24-bit buffer for decoding
1022
1023
/* writing bit pos inside bits; one of 24 (left, msb), 18, 12, 6, 0 */
1024
private int wpos = 0;
1025
1026
/* reading bit pos inside bits: one of 24 (left, msb), 16, 8, 0 */
1027
private int rpos = 0;
1028
1029
private boolean eof = false;
1030
private boolean closed = false;
1031
1032
DecInputStream(InputStream is, int[] base64, boolean isMIME) {
1033
this.is = is;
1034
this.base64 = base64;
1035
this.isMIME = isMIME;
1036
}
1037
1038
private byte[] sbBuf = new byte[1];
1039
1040
@Override
1041
public int read() throws IOException {
1042
return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff;
1043
}
1044
1045
private int leftovers(byte[] b, int off, int pos, int limit) {
1046
eof = true;
1047
1048
/*
1049
* We use a loop here, as this method is executed only a few times.
1050
* Unrolling the loop would probably not contribute much here.
1051
*/
1052
while (rpos - 8 >= wpos && pos != limit) {
1053
rpos -= 8;
1054
b[pos++] = (byte) (bits >> rpos);
1055
}
1056
return pos - off != 0 || rpos - 8 >= wpos ? pos - off : -1;
1057
}
1058
1059
private int eof(byte[] b, int off, int pos, int limit) throws IOException {
1060
/*
1061
* pos != limit
1062
*
1063
* wpos == 18: x dangling single x, invalid unit
1064
* accept ending xx or xxx without padding characters
1065
*/
1066
if (wpos == 18) {
1067
throw new IOException("Base64 stream has one un-decoded dangling byte.");
1068
}
1069
rpos = 24;
1070
return leftovers(b, off, pos, limit);
1071
}
1072
1073
private int padding(byte[] b, int off, int pos, int limit) throws IOException {
1074
/*
1075
* pos != limit
1076
*
1077
* wpos == 24: = (unnecessary padding)
1078
* wpos == 18: x= (dangling single x, invalid unit)
1079
* wpos == 12 and missing last '=': xx= (invalid padding)
1080
* wpos == 12 and last is not '=': xx=x (invalid padding)
1081
*/
1082
if (wpos >= 18 || wpos == 12 && is.read() != '=') {
1083
throw new IOException("Illegal base64 ending sequence:" + wpos);
1084
}
1085
rpos = 24;
1086
return leftovers(b, off, pos, limit);
1087
}
1088
1089
@Override
1090
public int read(byte[] b, int off, int len) throws IOException {
1091
if (closed) {
1092
throw new IOException("Stream is closed");
1093
}
1094
Objects.checkFromIndexSize(off, len, b.length);
1095
if (len == 0) {
1096
return 0;
1097
}
1098
1099
/*
1100
* Rather than keeping 2 running vars (e.g., off and len),
1101
* we only keep one (pos), while definitely fixing the boundaries
1102
* of the range [off, limit).
1103
* More specifically, each use of pos as an index in b meets
1104
* pos - off >= 0 & limit - pos > 0
1105
*
1106
* Note that limit can overflow to Integer.MIN_VALUE. However,
1107
* as long as comparisons with pos are as coded, there's no harm.
1108
*/
1109
int pos = off;
1110
final int limit = off + len;
1111
if (eof) {
1112
return leftovers(b, off, pos, limit);
1113
}
1114
1115
/*
1116
* Leftovers from previous invocation; here, wpos = 0.
1117
* There can be at most 2 leftover bytes (rpos <= 16).
1118
* Further, b has at least one free place.
1119
*
1120
* The logic could be coded as a loop, (as in method leftovers())
1121
* but the explicit "unrolling" makes it possible to generate
1122
* better byte extraction code.
1123
*/
1124
if (rpos == 16) {
1125
b[pos++] = (byte) (bits >> 8);
1126
rpos = 8;
1127
if (pos == limit) {
1128
return len;
1129
}
1130
}
1131
if (rpos == 8) {
1132
b[pos++] = (byte) bits;
1133
rpos = 0;
1134
if (pos == limit) {
1135
return len;
1136
}
1137
}
1138
1139
bits = 0;
1140
wpos = 24;
1141
for (;;) {
1142
/* pos != limit & rpos == 0 */
1143
final int i = is.read();
1144
if (i < 0) {
1145
return eof(b, off, pos, limit);
1146
}
1147
final int v = base64[i];
1148
if (v < 0) {
1149
/*
1150
* i not in alphabet, thus
1151
* v == -2: i is '=', the padding
1152
* v == -1: i is something else, typically CR or LF
1153
*/
1154
if (v == -1) {
1155
if (isMIME) {
1156
continue;
1157
}
1158
throw new IOException("Illegal base64 character 0x" +
1159
Integer.toHexString(i));
1160
}
1161
return padding(b, off, pos, limit);
1162
}
1163
wpos -= 6;
1164
bits |= v << wpos;
1165
if (wpos != 0) {
1166
continue;
1167
}
1168
if (limit - pos >= 3) {
1169
/* frequently taken fast path, no need to track rpos */
1170
b[pos++] = (byte) (bits >> 16);
1171
b[pos++] = (byte) (bits >> 8);
1172
b[pos++] = (byte) bits;
1173
bits = 0;
1174
wpos = 24;
1175
if (pos == limit) {
1176
return len;
1177
}
1178
continue;
1179
}
1180
1181
/* b has either 1 or 2 free places */
1182
b[pos++] = (byte) (bits >> 16);
1183
if (pos == limit) {
1184
rpos = 16;
1185
return len;
1186
}
1187
b[pos++] = (byte) (bits >> 8);
1188
/* pos == limit, no need for an if */
1189
rpos = 8;
1190
return len;
1191
}
1192
}
1193
1194
@Override
1195
public int available() throws IOException {
1196
if (closed)
1197
throw new IOException("Stream is closed");
1198
return is.available(); // TBD:
1199
}
1200
1201
@Override
1202
public void close() throws IOException {
1203
if (!closed) {
1204
closed = true;
1205
is.close();
1206
}
1207
}
1208
}
1209
}
1210
1211