1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.util;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.hadoop.hbase.classification.InterfaceAudience;
25 import org.apache.hadoop.hbase.classification.InterfaceStability;
26
27 import java.io.BufferedInputStream;
28 import java.io.ByteArrayInputStream;
29 import java.io.ByteArrayOutputStream;
30 import java.io.File;
31 import java.io.FileInputStream;
32 import java.io.FilterInputStream;
33 import java.io.FilterOutputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.OutputStream;
37 import java.io.UnsupportedEncodingException;
38 import java.util.zip.GZIPInputStream;
39 import java.util.zip.GZIPOutputStream;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 @InterfaceAudience.Private
116 @InterfaceStability.Stable
117 public class Base64 {
118
119
120
121
122 public final static int NO_OPTIONS = 0;
123
124
125 public final static int ENCODE = 1;
126
127
128 public final static int DECODE = 0;
129
130
131 public final static int GZIP = 2;
132
133
134 public final static int DONT_BREAK_LINES = 8;
135
136
137
138
139
140
141
142
143
144
145
146 public final static int URL_SAFE = 16;
147
148
149
150
151
152
153 public final static int ORDERED = 32;
154
155
156
157 private static final Log LOG = LogFactory.getLog(Base64.class);
158
159
160 private final static int MAX_LINE_LENGTH = 76;
161
162
163 private final static byte EQUALS_SIGN = (byte) '=';
164
165
166 private final static byte NEW_LINE = (byte) '\n';
167
168
169 private final static String PREFERRED_ENCODING = "UTF-8";
170
171 private final static byte WHITE_SPACE_ENC = -5;
172 private final static byte EQUALS_SIGN_ENC = -1;
173
174
175
176
177
178
179
180
181
182 private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B',
183 (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
184 (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
185 (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
186 (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
187 (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
188 (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
189 (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
190 (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
191 (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
192 (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
193 (byte) '+', (byte) '/'
194 };
195
196
197
198
199
200 private final static byte[] _STANDARD_DECODABET = {
201 -9, -9, -9, -9, -9, -9, -9, -9, -9,
202 -5, -5,
203 -9, -9,
204 -5,
205 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
206 -9, -9, -9, -9, -9,
207 -5,
208 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
209 62,
210 -9, -9, -9,
211 63,
212 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
213 -9, -9, -9,
214 -1,
215 -9, -9, -9,
216 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
217 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
218 -9, -9, -9, -9, -9, -9,
219 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
220 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
221 -9, -9, -9, -9
222 };
223
224
225
226
227
228
229
230
231
232
233 private final static byte[] _URL_SAFE_ALPHABET = { (byte) 'A', (byte) 'B',
234 (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
235 (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
236 (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
237 (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
238 (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
239 (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
240 (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
241 (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
242 (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
243 (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
244 (byte) '-', (byte) '_'
245 };
246
247
248
249
250 private final static byte[] _URL_SAFE_DECODABET = {
251 -9, -9, -9, -9, -9, -9, -9, -9, -9,
252 -5, -5,
253 -9, -9,
254 -5,
255 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
256 -9, -9, -9, -9, -9,
257 -5,
258 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
259 -9,
260 -9,
261 62,
262 -9,
263 -9,
264 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
265 -9, -9, -9,
266 -1,
267 -9, -9, -9,
268 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
269 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
270 -9, -9, -9, -9,
271 63,
272 -9,
273 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
274 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
275 -9, -9, -9, -9
276 };
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292 private final static byte[] _ORDERED_ALPHABET = { (byte) '-', (byte) '0',
293 (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
294 (byte) '7', (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C',
295 (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I',
296 (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O',
297 (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
298 (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) '_',
299 (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
300 (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
301 (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
302 (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
303 (byte) 'y', (byte) 'z'
304 };
305
306
307
308
309 private final static byte[] _ORDERED_DECODABET = {
310 -9, -9, -9, -9, -9, -9, -9, -9, -9,
311 -5, -5,
312 -9, -9,
313 -5,
314 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
315 -9, -9, -9, -9, -9,
316 -5,
317 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
318 -9,
319 -9,
320 0,
321 -9,
322 -9,
323 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
324 -9, -9, -9,
325 -1,
326 -9, -9, -9,
327 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
328 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
329 -9, -9, -9, -9,
330 37,
331 -9,
332 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
333 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
334 -9, -9, -9, -9
335 };
336
337
338
339
340
341
342
343
344
345
346
347
348 protected static byte[] getAlphabet(int options) {
349 if ((options & URL_SAFE) == URL_SAFE) {
350 return _URL_SAFE_ALPHABET;
351
352 } else if ((options & ORDERED) == ORDERED) {
353 return _ORDERED_ALPHABET;
354
355 } else {
356 return _STANDARD_ALPHABET;
357 }
358 }
359
360
361
362
363
364
365
366
367
368 protected static byte[] getDecodabet(int options) {
369 if ((options & URL_SAFE) == URL_SAFE) {
370 return _URL_SAFE_DECODABET;
371
372 } else if ((options & ORDERED) == ORDERED) {
373 return _ORDERED_DECODABET;
374
375 } else {
376 return _STANDARD_DECODABET;
377 }
378 }
379
380
381 private Base64() {}
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399 protected static byte[] encode3to4(byte[] b4, byte[] threeBytes,
400 int numSigBytes, int options) {
401 encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
402 return b4;
403 }
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428 protected static byte[] encode3to4(byte[] source, int srcOffset,
429 int numSigBytes, byte[] destination, int destOffset, int options) {
430 byte[] ALPHABET = getAlphabet(options);
431
432
433
434
435
436
437
438
439
440
441
442
443 int inBuff =
444 (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
445 | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
446 | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
447
448 switch (numSigBytes) {
449 case 3:
450 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
451 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
452 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
453 destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
454 return destination;
455
456 case 2:
457 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
458 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
459 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
460 destination[destOffset + 3] = EQUALS_SIGN;
461 return destination;
462
463 case 1:
464 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
465 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
466 destination[destOffset + 2] = EQUALS_SIGN;
467 destination[destOffset + 3] = EQUALS_SIGN;
468 return destination;
469
470 default:
471 return destination;
472 }
473 }
474
475
476
477
478
479
480
481
482 public static String encodeBytes(byte[] source) {
483 return encodeBytes(source, 0, source.length, NO_OPTIONS);
484 }
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511 public static String encodeBytes(byte[] source, int options) {
512 return encodeBytes(source, 0, source.length, options);
513 }
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542 public static String encodeBytes(byte[] source, int off, int len, int options) {
543 if ((options & GZIP) == GZIP) {
544
545 ByteArrayOutputStream baos = new ByteArrayOutputStream();
546 GZIPOutputStream gzos = null;
547
548 try {
549 gzos =
550 new GZIPOutputStream(new Base64OutputStream(baos, ENCODE | options));
551
552 gzos.write(source, off, len);
553 gzos.close();
554 gzos = null;
555 return new String(baos.toByteArray(), PREFERRED_ENCODING);
556
557 } catch (UnsupportedEncodingException uue) {
558 return new String(baos.toByteArray());
559
560 } catch (IOException e) {
561 LOG.error("error encoding byte array", e);
562 return null;
563
564 } finally {
565 if (gzos != null) {
566 try {
567 gzos.close();
568 } catch (Exception e) {
569 LOG.error("error closing GZIPOutputStream", e);
570 }
571 }
572 try {
573 baos.close();
574 } catch (Exception e) {
575 LOG.error("error closing ByteArrayOutputStream", e);
576 }
577 }
578
579 }
580
581
582
583 boolean breakLines = ((options & DONT_BREAK_LINES) == 0);
584
585 int len43 = len * 4 / 3;
586 byte[] outBuff =
587 new byte[(len43)
588 + ((len % 3) > 0 ? 4 : 0)
589 + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)];
590 int d = 0;
591 int e = 0;
592 int len2 = len - 2;
593 int lineLength = 0;
594 for (; d < len2; d += 3, e += 4) {
595 encode3to4(source, d + off, 3, outBuff, e, options);
596
597 lineLength += 4;
598 if (breakLines && lineLength == MAX_LINE_LENGTH) {
599 outBuff[e + 4] = NEW_LINE;
600 e++;
601 lineLength = 0;
602 }
603 }
604
605 if (d < len) {
606 encode3to4(source, d + off, len - d, outBuff, e, options);
607 e += 4;
608 }
609
610
611 try {
612 return new String(outBuff, 0, e, PREFERRED_ENCODING);
613
614 } catch (UnsupportedEncodingException uue) {
615 return new String(outBuff, 0, e);
616 }
617 }
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646 @SuppressWarnings({"ConstantConditions"})
647 protected static int decode4to3(byte[] source, int srcOffset,
648 byte[] destination, int destOffset, int options) {
649 byte[] DECODABET = getDecodabet(options);
650
651 if (source[srcOffset + 2] == EQUALS_SIGN) {
652
653
654
655 int outBuff =
656 ((DECODABET[source[srcOffset]] & 0xFF) << 18)
657 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
658
659 destination[destOffset] = (byte) (outBuff >>> 16);
660 return 1;
661
662 } else if (source[srcOffset + 3] == EQUALS_SIGN) {
663
664
665
666
667 int outBuff =
668 ((DECODABET[source[srcOffset]] & 0xFF) << 18)
669 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
670 | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
671
672 destination[destOffset] = (byte) (outBuff >>> 16);
673 destination[destOffset + 1] = (byte) (outBuff >>> 8);
674 return 2;
675
676 } else {
677 try {
678
679
680
681
682
683 int outBuff =
684 ((DECODABET[source[srcOffset]] & 0xFF) << 18)
685 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
686 | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
687 | ((DECODABET[source[srcOffset + 3]] & 0xFF));
688
689 destination[destOffset] = (byte) (outBuff >> 16);
690 destination[destOffset + 1] = (byte) (outBuff >> 8);
691 destination[destOffset + 2] = (byte) (outBuff);
692
693 return 3;
694
695 } catch (Exception e) {
696 LOG.error("error decoding bytes at " + source[srcOffset] + ": " +
697 (DECODABET[source[srcOffset]]) + ", " + source[srcOffset + 1] +
698 ": " + (DECODABET[source[srcOffset + 1]]) + ", " +
699 source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]]) +
700 ", " + source[srcOffset + 3] + ": " +
701 (DECODABET[source[srcOffset + 3]]), e);
702 return -1;
703 }
704 }
705 }
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721 public static byte[] decode(byte[] source, int off, int len, int options) {
722 byte[] DECODABET = getDecodabet(options);
723
724 int len34 = len * 3 / 4;
725 byte[] outBuff = new byte[len34];
726 int outBuffPosn = 0;
727
728 byte[] b4 = new byte[4];
729 int b4Posn = 0;
730 int i;
731 byte sbiCrop;
732 byte sbiDecode;
733 for (i = off; i < off + len; i++) {
734 sbiCrop = (byte) (source[i] & 0x7f);
735 sbiDecode = DECODABET[sbiCrop];
736
737 if (sbiDecode >= WHITE_SPACE_ENC) {
738 if (sbiDecode >= EQUALS_SIGN_ENC) {
739 b4[b4Posn++] = sbiCrop;
740 if (b4Posn > 3) {
741 outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options);
742 b4Posn = 0;
743
744
745 if (sbiCrop == EQUALS_SIGN)
746 break;
747 }
748 }
749 } else {
750 LOG.error("Bad Base64 input character at " + i + ": " + source[i] +
751 "(decimal)");
752 return null;
753 }
754 }
755
756 byte[] out = new byte[outBuffPosn];
757 System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
758 return out;
759 }
760
761
762
763
764
765
766
767
768
769 public static byte[] decode(String s) {
770 return decode(s, NO_OPTIONS);
771 }
772
773
774
775
776
777
778
779
780
781
782
783
784 public static byte[] decode(String s, int options) {
785 byte[] bytes;
786 try {
787 bytes = s.getBytes(PREFERRED_ENCODING);
788
789 } catch (UnsupportedEncodingException uee) {
790 bytes = s.getBytes();
791 }
792
793
794
795 bytes = decode(bytes, 0, bytes.length, options);
796
797
798
799
800 if (bytes != null && bytes.length >= 4) {
801 int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
802 if (GZIPInputStream.GZIP_MAGIC == head) {
803 GZIPInputStream gzis = null;
804 ByteArrayOutputStream baos = new ByteArrayOutputStream();
805 try {
806 gzis = new GZIPInputStream(new ByteArrayInputStream(bytes));
807
808 byte[] buffer = new byte[2048];
809 for (int length; (length = gzis.read(buffer)) >= 0; ) {
810 baos.write(buffer, 0, length);
811 }
812
813
814 bytes = baos.toByteArray();
815
816 } catch (IOException e) {
817
818
819 } finally {
820 try {
821 baos.close();
822 } catch (Exception e) {
823 LOG.error("error closing ByteArrayOutputStream", e);
824 }
825 if (gzis != null) {
826 try {
827 gzis.close();
828 } catch (Exception e) {
829 LOG.error("error closing GZIPInputStream", e);
830 }
831 }
832 }
833 }
834 }
835
836 return bytes;
837 }
838
839
840
841
842
843
844
845
846
847 public static byte[] decodeFromFile(String filename) {
848 byte[] decodedData = null;
849 Base64InputStream bis = null;
850 try {
851 File file = new File(filename);
852 byte[] buffer;
853
854
855 if (file.length() > Integer.MAX_VALUE) {
856 LOG.fatal("File is too big for this convenience method (" +
857 file.length() + " bytes).");
858 return null;
859 }
860
861 buffer = new byte[(int) file.length()];
862
863
864
865 bis = new Base64InputStream(new BufferedInputStream(
866 new FileInputStream(file)), DECODE);
867
868
869
870 int length = 0;
871 for (int numBytes; (numBytes = bis.read(buffer, length, 4096)) >= 0; ) {
872 length += numBytes;
873 }
874
875
876
877 decodedData = new byte[length];
878 System.arraycopy(buffer, 0, decodedData, 0, length);
879
880 } catch (IOException e) {
881 LOG.error("Error decoding from file " + filename, e);
882
883 } finally {
884 if (bis != null) {
885 try {
886 bis.close();
887 } catch (Exception e) {
888 LOG.error("error closing Base64InputStream", e);
889 }
890 }
891 }
892
893 return decodedData;
894 }
895
896
897
898
899
900
901
902
903
904 public static String encodeFromFile(String filename) {
905 String encodedData = null;
906 Base64InputStream bis = null;
907 try {
908 File file = new File(filename);
909
910
911
912 byte[] buffer = new byte[Math.max((int) (file.length() * 1.4), 40)];
913
914
915
916 bis = new Base64InputStream(new BufferedInputStream(
917 new FileInputStream(file)), ENCODE);
918
919
920 int length = 0;
921 for (int numBytes; (numBytes = bis.read(buffer, length, 4096)) >= 0; ) {
922 length += numBytes;
923 }
924
925
926
927 encodedData = new String(buffer, 0, length, PREFERRED_ENCODING);
928
929 } catch (IOException e) {
930 LOG.error("Error encoding from file " + filename, e);
931
932 } finally {
933 if (bis != null) {
934 try {
935 bis.close();
936 } catch (Exception e) {
937 LOG.error("error closing Base64InputStream", e);
938 }
939 }
940 }
941
942 return encodedData;
943 }
944
945
946
947
948
949
950
951
952
953
954
955 @InterfaceAudience.Private
956 @InterfaceStability.Stable
957 public static class Base64InputStream extends FilterInputStream {
958 private boolean encode;
959 private int position;
960 private byte[] buffer;
961 private int bufferLength;
962 private int numSigBytes;
963 private int lineLength;
964 private boolean breakLines;
965 private int options;
966 private byte[] decodabet;
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991 public Base64InputStream(InputStream in, int options) {
992 super(in);
993 this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
994 this.encode = (options & ENCODE) == ENCODE;
995 this.bufferLength = encode ? 4 : 3;
996 this.buffer = new byte[bufferLength];
997 this.position = -1;
998 this.lineLength = 0;
999 this.options = options;
1000
1001 this.decodabet = getDecodabet(options);
1002 }
1003
1004
1005
1006
1007
1008
1009
1010
1011 @Override
1012 public int read() throws IOException {
1013
1014 if (position < 0) {
1015 if (encode) {
1016 byte[] b3 = new byte[3];
1017 int numBinaryBytes = 0;
1018 for (int i = 0; i < 3; i++) {
1019 try {
1020 int b = in.read();
1021
1022
1023 if (b >= 0) {
1024 b3[i] = (byte) b;
1025 numBinaryBytes++;
1026 }
1027
1028 } catch (IOException e) {
1029
1030 if (i == 0)
1031 throw e;
1032
1033 }
1034 }
1035
1036 if (numBinaryBytes > 0) {
1037 encode3to4(b3, 0, numBinaryBytes, buffer, 0, options);
1038 position = 0;
1039 numSigBytes = 4;
1040
1041 } else {
1042 return -1;
1043 }
1044
1045 } else {
1046 byte[] b4 = new byte[4];
1047 int i;
1048 for (i = 0; i < 4; i++) {
1049
1050 int b;
1051 do {
1052 b = in.read();
1053 } while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC);
1054
1055 if (b < 0) {
1056 break;
1057 }
1058
1059 b4[i] = (byte) b;
1060 }
1061
1062 if (i == 4) {
1063 numSigBytes = decode4to3(b4, 0, buffer, 0, options);
1064 position = 0;
1065
1066 } else if (i == 0) {
1067 return -1;
1068
1069 } else {
1070
1071 throw new IOException("Improperly padded Base64 input.");
1072 }
1073 }
1074 }
1075
1076
1077 if (position >= 0) {
1078
1079 if (
1080 return -1;
1081 }
1082
1083 if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
1084 lineLength = 0;
1085 return '\n';
1086
1087 }
1088 lineLength++;
1089
1090
1091
1092 int b = buffer[position++];
1093
1094 if (position >= bufferLength)
1095 position = -1;
1096
1097 return b & 0xFF;
1098
1099
1100 }
1101
1102
1103 throw new IOException("Error in Base64 code reading stream.");
1104
1105 }
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118 @Override
1119 public int read(byte[] dest, int off, int len) throws IOException {
1120 int i;
1121 int b;
1122 for (i = 0; i < len; i++) {
1123 b = read();
1124 if (b >= 0) {
1125 dest[off + i] = (byte) b;
1126 } else if (i == 0) {
1127 return -1;
1128 } else {
1129 break;
1130 }
1131 }
1132 return i;
1133 }
1134
1135 }
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147 @InterfaceAudience.Private
1148 @InterfaceStability.Stable
1149 public static class Base64OutputStream extends FilterOutputStream {
1150 private boolean encode;
1151 private int position;
1152 private byte[] buffer;
1153 private int bufferLength;
1154 private int lineLength;
1155 private boolean breakLines;
1156 private byte[] b4;
1157 private boolean suspendEncoding;
1158 private int options;
1159 private byte[] decodabet;
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183 @InterfaceAudience.Private
1184 @InterfaceStability.Stable
1185 public Base64OutputStream(OutputStream out, int options) {
1186 super(out);
1187 this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1188 this.encode = (options & ENCODE) == ENCODE;
1189 this.bufferLength = encode ? 3 : 4;
1190 this.buffer = new byte[bufferLength];
1191 this.position = 0;
1192 this.lineLength = 0;
1193 this.suspendEncoding = false;
1194 this.b4 = new byte[4];
1195 this.options = options;
1196 this.decodabet = getDecodabet(options);
1197 }
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208 @Override
1209 public void write(int theByte) throws IOException {
1210
1211 if (suspendEncoding) {
1212 super.out.write(theByte);
1213 return;
1214 }
1215
1216
1217 if (encode) {
1218 buffer[position++] = (byte) theByte;
1219 if (position >= bufferLength) {
1220 out.write(encode3to4(b4, buffer, bufferLength, options));
1221 lineLength += 4;
1222 if (breakLines && lineLength >= MAX_LINE_LENGTH) {
1223 out.write(NEW_LINE);
1224 lineLength = 0;
1225 }
1226
1227 position = 0;
1228 }
1229
1230 } else {
1231
1232 if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) {
1233 buffer[position++] = (byte) theByte;
1234 if (position >= bufferLength) {
1235 int len = decode4to3(buffer, 0, b4, 0, options);
1236 out.write(b4, 0, len);
1237 position = 0;
1238 }
1239
1240 } else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
1241 throw new IOException("Invalid character in Base64 data.");
1242 }
1243 }
1244 }
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255 @Override
1256 public void write(byte[] theBytes, int off, int len) throws IOException {
1257
1258 if (suspendEncoding) {
1259 super.out.write(theBytes, off, len);
1260 return;
1261 }
1262
1263 for (int i = 0; i < len; i++) {
1264 write(theBytes[off + i]);
1265 }
1266
1267 }
1268
1269
1270
1271
1272
1273
1274
1275 public void flushBase64() throws IOException {
1276 if (position > 0) {
1277 if (encode) {
1278 out.write(encode3to4(b4, buffer, position, options));
1279 position = 0;
1280
1281 } else {
1282 throw new IOException("Base64 input not properly padded.");
1283 }
1284 }
1285
1286 }
1287
1288
1289
1290
1291
1292
1293 @Override
1294 public void close() throws IOException {
1295
1296 flushBase64();
1297
1298
1299
1300 super.close();
1301
1302 buffer = null;
1303 out = null;
1304 }
1305
1306 }
1307
1308 }