2 // NOTE: The following source code is the iHarder.net public domain
3 // Base64 library and is provided here as a convenience. For updates,
4 // problems, questions, etc. regarding this code, please visit:
5 // http://iharder.sourceforge.net/current/java/base64/
8 package org
.spearce
.jgit
.util
;
12 * Encodes and decodes to and from Base64 notation.
18 * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
19 * some convenience methods for reading and writing to and from files.</li>
20 * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
21 * with other encodings (like EBCDIC).</li>
22 * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
23 * encoded data was a single byte.</li>
24 * <li>v2.0 - I got rid of methods that used booleans to set options.
25 * Now everything is more consolidated and cleaner. The code now detects
26 * when data that's being decoded is gzip-compressed and will decompress it
27 * automatically. Generally things are cleaner. You'll probably have to
28 * change some method calls that you were making to support the new
29 * options format (<tt>int</tt>s that you "OR" together).</li>
30 * <li>v1.5.1 - Fixed bug when decompressing and decoding to a
31 * byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
32 * Added the ability to "suspend" encoding in the Output Stream so
33 * you can turn on and off the encoding if you need to embed base64
34 * data in an otherwise "normal" stream (like an XML file).</li>
35 * <li>v1.5 - Output stream passes on flush() command but doesn't do anything itself.
36 * This helps when using GZIP streams.
37 * Added the ability to GZip-compress objects before encoding them.</li>
38 * <li>v1.4 - Added helper methods to read/write files.</li>
39 * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
40 * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
41 * where last buffer being read, if not completely full, was not returned.</li>
42 * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
43 * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
47 * I am placing this code in the Public Domain. Do with it as you will.
48 * This software comes with no guarantees or warranties but with
49 * plenty of well-wishing instead!
50 * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
51 * periodically to check for updates or to contribute improvements.
54 * @author Robert Harder
55 * @author rob@iharder.net
61 /* ******** P U B L I C F I E L D S ******** */
64 /** No options specified. Value is zero. */
65 public final static int NO_OPTIONS
= 0;
67 /** Specify encoding. */
68 public final static int ENCODE
= 1;
71 /** Specify decoding. */
72 public final static int DECODE
= 0;
75 /** Specify that data should be gzip-compressed. */
76 public final static int GZIP
= 2;
79 /** Don't break lines when encoding (violates strict Base64 specification) */
80 public final static int DONT_BREAK_LINES
= 8;
83 /* ******** P R I V A T E F I E L D S ******** */
86 /** Maximum line length (76) of Base64 output. */
87 private final static int MAX_LINE_LENGTH
= 76;
90 /** The equals sign (=) as a byte. */
91 private final static byte EQUALS_SIGN
= (byte)'=';
94 /** The new line character (\n) as a byte. */
95 private final static byte NEW_LINE
= (byte)'\n';
98 /** Preferred encoding. */
99 private final static String PREFERRED_ENCODING
= "UTF-8";
102 /** The 64 valid Base64 values. */
103 private final static byte[] ALPHABET
;
104 private final static byte[] _NATIVE_ALPHABET
= /* May be something funny like EBCDIC */
106 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
107 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
108 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
109 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
110 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
111 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
112 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
113 (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
114 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
115 (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
118 /** Determine which ALPHABET to use. */
124 __bytes
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING
);
126 catch (java
.io
.UnsupportedEncodingException use
)
128 __bytes
= _NATIVE_ALPHABET
; // Fall back to native encoding
135 * Translates a Base64 value to either its 6-bit reconstruction value
136 * or a negative number indicating some other meaning.
138 private final static byte[] DECODABET
=
140 -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
141 -5,-5, // Whitespace: Tab and Linefeed
142 -9,-9, // Decimal 11 - 12
143 -5, // Whitespace: Carriage Return
144 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
145 -9,-9,-9,-9,-9, // Decimal 27 - 31
146 -5, // Whitespace: Space
147 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
148 62, // Plus sign at decimal 43
149 -9,-9,-9, // Decimal 44 - 46
150 63, // Slash at decimal 47
151 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
152 -9,-9,-9, // Decimal 58 - 60
153 -1, // Equals sign at decimal 61
154 -9,-9,-9, // Decimal 62 - 64
155 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
156 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
157 -9,-9,-9,-9,-9,-9, // Decimal 91 - 96
158 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
159 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
160 -9,-9,-9,-9 // Decimal 123 - 126
161 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
162 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
163 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
164 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
165 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
166 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
167 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
168 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
169 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
170 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
173 // I think I end up not using the BAD_ENCODING indicator.
174 //private final static byte BAD_ENCODING = -9; // Indicates error in encoding
175 private final static byte WHITE_SPACE_ENC
= -5; // Indicates white space in encoding
176 private final static byte EQUALS_SIGN_ENC
= -1; // Indicates equals sign in encoding
179 /** Defeats instantiation. */
184 /* ******** E N C O D I N G M E T H O D S ******** */
188 * Encodes up to the first three bytes of array <var>threeBytes</var>
189 * and returns a four-byte array in Base64 notation.
190 * The actual number of significant bytes in your array is
191 * given by <var>numSigBytes</var>.
192 * The array <var>threeBytes</var> needs only be as big as
193 * <var>numSigBytes</var>.
194 * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
196 * @param b4 A reusable byte array to reduce array instantiation
197 * @param threeBytes the array to convert
198 * @param numSigBytes the number of significant bytes in your array
199 * @return four byte array in Base64 notation.
202 private static byte[] encode3to4( byte[] b4
, byte[] threeBytes
, int numSigBytes
)
204 encode3to4( threeBytes
, 0, numSigBytes
, b4
, 0 );
210 * Encodes up to three bytes of the array <var>source</var>
211 * and writes the resulting four Base64 bytes to <var>destination</var>.
212 * The source and destination arrays can be manipulated
213 * anywhere along their length by specifying
214 * <var>srcOffset</var> and <var>destOffset</var>.
215 * This method does not check to make sure your arrays
216 * are large enough to accommodate <var>srcOffset</var> + 3 for
217 * the <var>source</var> array or <var>destOffset</var> + 4 for
218 * the <var>destination</var> array.
219 * The actual number of significant bytes in your array is
220 * given by <var>numSigBytes</var>.
222 * @param source the array to convert
223 * @param srcOffset the index where conversion begins
224 * @param numSigBytes the number of significant bytes in your array
225 * @param destination the array to hold the conversion
226 * @param destOffset the index where output will be put
227 * @return the <var>destination</var> array
230 private static byte[] encode3to4(
231 byte[] source
, int srcOffset
, int numSigBytes
,
232 byte[] destination
, int destOffset
)
235 // 01234567890123456789012345678901 Bit position
236 // --------000000001111111122222222 Array position from threeBytes
237 // --------| || || || | Six bit groups to index ALPHABET
238 // >>18 >>12 >> 6 >> 0 Right shift necessary
239 // 0x3f 0x3f 0x3f Additional AND
241 // Create buffer with zero-padding if there are only one or two
242 // significant bytes passed in the array.
243 // We have to shift left 24 in order to flush out the 1's that appear
244 // when Java treats a value as negative that is cast from a byte to an int.
245 int inBuff
= ( numSigBytes
> 0 ?
((source
[ srcOffset
] << 24) >>> 8) : 0 )
246 | ( numSigBytes
> 1 ?
((source
[ srcOffset
+ 1 ] << 24) >>> 16) : 0 )
247 | ( numSigBytes
> 2 ?
((source
[ srcOffset
+ 2 ] << 24) >>> 24) : 0 );
249 switch( numSigBytes
)
252 destination
[ destOffset
] = ALPHABET
[ (inBuff
>>> 18) ];
253 destination
[ destOffset
+ 1 ] = ALPHABET
[ (inBuff
>>> 12) & 0x3f ];
254 destination
[ destOffset
+ 2 ] = ALPHABET
[ (inBuff
>>> 6) & 0x3f ];
255 destination
[ destOffset
+ 3 ] = ALPHABET
[ (inBuff
) & 0x3f ];
259 destination
[ destOffset
] = ALPHABET
[ (inBuff
>>> 18) ];
260 destination
[ destOffset
+ 1 ] = ALPHABET
[ (inBuff
>>> 12) & 0x3f ];
261 destination
[ destOffset
+ 2 ] = ALPHABET
[ (inBuff
>>> 6) & 0x3f ];
262 destination
[ destOffset
+ 3 ] = EQUALS_SIGN
;
266 destination
[ destOffset
] = ALPHABET
[ (inBuff
>>> 18) ];
267 destination
[ destOffset
+ 1 ] = ALPHABET
[ (inBuff
>>> 12) & 0x3f ];
268 destination
[ destOffset
+ 2 ] = EQUALS_SIGN
;
269 destination
[ destOffset
+ 3 ] = EQUALS_SIGN
;
280 * Serializes an object and returns the Base64-encoded
281 * version of that serialized object. If the object
282 * cannot be serialized or there is another error,
283 * the method will return <tt>null</tt>.
284 * The object is not GZip-compressed before being encoded.
286 * @param serializableObject The object to encode
287 * @return The Base64-encoded object
290 public static String
encodeObject( java
.io
.Serializable serializableObject
)
292 return encodeObject( serializableObject
, NO_OPTIONS
);
293 } // end encodeObject
298 * Serializes an object and returns the Base64-encoded
299 * version of that serialized object. If the object
300 * cannot be serialized or there is another error,
301 * the method will return <tt>null</tt>.
303 * Valid options:<pre>
304 * GZIP: gzip-compresses object before encoding it.
305 * DONT_BREAK_LINES: don't break lines at 76 characters
306 * <i>Note: Technically, this makes your encoding non-compliant.</i>
309 * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
311 * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
313 * @param serializableObject The object to encode
314 * @param options Specified options
315 * @return The Base64-encoded object
317 * @see Base64#DONT_BREAK_LINES
320 public static String
encodeObject( java
.io
.Serializable serializableObject
, int options
)
323 java
.io
.ByteArrayOutputStream baos
= null;
324 java
.io
.OutputStream b64os
= null;
325 java
.io
.ObjectOutputStream oos
= null;
326 java
.util
.zip
.GZIPOutputStream gzos
= null;
329 int gzip
= (options
& GZIP
);
330 int dontBreakLines
= (options
& DONT_BREAK_LINES
);
334 // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
335 baos
= new java
.io
.ByteArrayOutputStream();
336 b64os
= new Base64
.OutputStream( baos
, ENCODE
| dontBreakLines
);
341 gzos
= new java
.util
.zip
.GZIPOutputStream( b64os
);
342 oos
= new java
.io
.ObjectOutputStream( gzos
);
345 oos
= new java
.io
.ObjectOutputStream( b64os
);
347 oos
.writeObject( serializableObject
);
349 catch( java
.io
.IOException e
)
356 try{ oos
.close(); } catch( Exception e
){}
357 try{ gzos
.close(); } catch( Exception e
){}
358 try{ b64os
.close(); } catch( Exception e
){}
359 try{ baos
.close(); } catch( Exception e
){}
362 // Return value according to relevant encoding.
365 return new String( baos
.toByteArray(), PREFERRED_ENCODING
);
367 catch (java
.io
.UnsupportedEncodingException uue
)
369 return new String( baos
.toByteArray() );
377 * Encodes a byte array into Base64 notation.
378 * Does not GZip-compress data.
380 * @param source The data to convert
381 * @return encoded base64 representation of source.
384 public static String
encodeBytes( byte[] source
)
386 return encodeBytes( source
, 0, source
.length
, NO_OPTIONS
);
392 * Encodes a byte array into Base64 notation.
394 * Valid options:<pre>
395 * GZIP: gzip-compresses object before encoding it.
396 * DONT_BREAK_LINES: don't break lines at 76 characters
397 * <i>Note: Technically, this makes your encoding non-compliant.</i>
400 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
402 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
405 * @param source The data to convert
406 * @param options Specified options
407 * @return encoded base64 representation of source.
409 * @see Base64#DONT_BREAK_LINES
412 public static String
encodeBytes( byte[] source
, int options
)
414 return encodeBytes( source
, 0, source
.length
, options
);
419 * Encodes a byte array into Base64 notation.
420 * Does not GZip-compress data.
422 * @param source The data to convert
423 * @param off Offset in array where conversion should begin
424 * @param len Length of data to convert
425 * @return encoded base64 representation of source.
428 public static String
encodeBytes( byte[] source
, int off
, int len
)
430 return encodeBytes( source
, off
, len
, NO_OPTIONS
);
436 * Encodes a byte array into Base64 notation.
438 * Valid options:<pre>
439 * GZIP: gzip-compresses object before encoding it.
440 * DONT_BREAK_LINES: don't break lines at 76 characters
441 * <i>Note: Technically, this makes your encoding non-compliant.</i>
444 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
446 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
449 * @param source The data to convert
450 * @param off Offset in array where conversion should begin
451 * @param len Length of data to convert
452 * @param options Specified options
453 * @return encoded base64 representation of source.
455 * @see Base64#DONT_BREAK_LINES
458 public static String
encodeBytes( byte[] source
, int off
, int len
, int options
)
461 int dontBreakLines
= ( options
& DONT_BREAK_LINES
);
462 int gzip
= ( options
& GZIP
);
467 java
.io
.ByteArrayOutputStream baos
= null;
468 java
.util
.zip
.GZIPOutputStream gzos
= null;
469 Base64
.OutputStream b64os
= null;
474 // GZip -> Base64 -> ByteArray
475 baos
= new java
.io
.ByteArrayOutputStream();
476 b64os
= new Base64
.OutputStream( baos
, ENCODE
| dontBreakLines
);
477 gzos
= new java
.util
.zip
.GZIPOutputStream( b64os
);
479 gzos
.write( source
, off
, len
);
482 catch( java
.io
.IOException e
)
489 try{ gzos
.close(); } catch( Exception e
){}
490 try{ b64os
.close(); } catch( Exception e
){}
491 try{ baos
.close(); } catch( Exception e
){}
494 // Return value according to relevant encoding.
497 return new String( baos
.toByteArray(), PREFERRED_ENCODING
);
499 catch (java
.io
.UnsupportedEncodingException uue
)
501 return new String( baos
.toByteArray() );
503 } // end if: compress
505 // Else, don't compress. Better not to use streams at all then.
508 // Convert option to boolean in way that code likes it.
509 boolean breakLines
= dontBreakLines
== 0;
511 int len43
= len
* 4 / 3;
512 byte[] outBuff
= new byte[ ( len43
) // Main 4:3
513 + ( (len
% 3) > 0 ?
4 : 0 ) // Account for padding
514 + (breakLines ?
( len43
/ MAX_LINE_LENGTH
) : 0) ]; // New lines
519 for( ; d
< len2
; d
+=3, e
+=4 )
521 encode3to4( source
, d
+off
, 3, outBuff
, e
);
524 if( breakLines
&& lineLength
== MAX_LINE_LENGTH
)
526 outBuff
[e
+4] = NEW_LINE
;
529 } // end if: end of line
530 } // end for: each piece of array
534 encode3to4( source
, d
+off
, len
- d
, outBuff
, e
);
536 } // end if: some padding needed
539 // Return value according to relevant encoding.
542 return new String( outBuff
, 0, e
, PREFERRED_ENCODING
);
544 catch (java
.io
.UnsupportedEncodingException uue
)
546 return new String( outBuff
, 0, e
);
549 } // end else: don't compress
557 /* ******** D E C O D I N G M E T H O D S ******** */
561 * Decodes four bytes from array <var>source</var>
562 * and writes the resulting bytes (up to three of them)
563 * to <var>destination</var>.
564 * The source and destination arrays can be manipulated
565 * anywhere along their length by specifying
566 * <var>srcOffset</var> and <var>destOffset</var>.
567 * This method does not check to make sure your arrays
568 * are large enough to accommodate <var>srcOffset</var> + 4 for
569 * the <var>source</var> array or <var>destOffset</var> + 3 for
570 * the <var>destination</var> array.
571 * This method returns the actual number of bytes that
572 * were converted from the Base64 encoding.
575 * @param source the array to convert
576 * @param srcOffset the index where conversion begins
577 * @param destination the array to hold the conversion
578 * @param destOffset the index where output will be put
579 * @return the number of decoded bytes converted
582 private static int decode4to3( byte[] source
, int srcOffset
, byte[] destination
, int destOffset
)
585 if( source
[ srcOffset
+ 2] == EQUALS_SIGN
)
587 // Two ways to do the same thing. Don't know which way I like best.
588 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
589 // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
590 int outBuff
= ( ( DECODABET
[ source
[ srcOffset
] ] & 0xFF ) << 18 )
591 | ( ( DECODABET
[ source
[ srcOffset
+ 1] ] & 0xFF ) << 12 );
593 destination
[ destOffset
] = (byte)( outBuff
>>> 16 );
598 else if( source
[ srcOffset
+ 3 ] == EQUALS_SIGN
)
600 // Two ways to do the same thing. Don't know which way I like best.
601 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
602 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
603 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
604 int outBuff
= ( ( DECODABET
[ source
[ srcOffset
] ] & 0xFF ) << 18 )
605 | ( ( DECODABET
[ source
[ srcOffset
+ 1 ] ] & 0xFF ) << 12 )
606 | ( ( DECODABET
[ source
[ srcOffset
+ 2 ] ] & 0xFF ) << 6 );
608 destination
[ destOffset
] = (byte)( outBuff
>>> 16 );
609 destination
[ destOffset
+ 1 ] = (byte)( outBuff
>>> 8 );
617 // Two ways to do the same thing. Don't know which way I like best.
618 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
619 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
620 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
621 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
622 int outBuff
= ( ( DECODABET
[ source
[ srcOffset
] ] & 0xFF ) << 18 )
623 | ( ( DECODABET
[ source
[ srcOffset
+ 1 ] ] & 0xFF ) << 12 )
624 | ( ( DECODABET
[ source
[ srcOffset
+ 2 ] ] & 0xFF ) << 6)
625 | ( ( DECODABET
[ source
[ srcOffset
+ 3 ] ] & 0xFF ) );
628 destination
[ destOffset
] = (byte)( outBuff
>> 16 );
629 destination
[ destOffset
+ 1 ] = (byte)( outBuff
>> 8 );
630 destination
[ destOffset
+ 2 ] = (byte)( outBuff
);
633 }catch( Exception e
){
634 System
.out
.println(""+source
[srcOffset
]+ ": " + ( DECODABET
[ source
[ srcOffset
] ] ) );
635 System
.out
.println(""+source
[srcOffset
+1]+ ": " + ( DECODABET
[ source
[ srcOffset
+ 1 ] ] ) );
636 System
.out
.println(""+source
[srcOffset
+2]+ ": " + ( DECODABET
[ source
[ srcOffset
+ 2 ] ] ) );
637 System
.out
.println(""+source
[srcOffset
+3]+ ": " + ( DECODABET
[ source
[ srcOffset
+ 3 ] ] ) );
641 } // end decodeToBytes
647 * Very low-level access to decoding ASCII characters in
648 * the form of a byte array. Does not support automatically
649 * gunzipping or any other "fancy" features.
651 * @param source The Base64 encoded data
652 * @param off The offset of where to begin decoding
653 * @param len The length of characters to decode
654 * @return decoded data
657 public static byte[] decode( byte[] source
, int off
, int len
)
659 int len34
= len
* 3 / 4;
660 byte[] outBuff
= new byte[ len34
]; // Upper limit on size of output
663 byte[] b4
= new byte[4];
668 for( i
= off
; i
< off
+len
; i
++ )
670 sbiCrop
= (byte)(source
[i
] & 0x7f); // Only the low seven bits
671 sbiDecode
= DECODABET
[ sbiCrop
];
673 if( sbiDecode
>= WHITE_SPACE_ENC
) // White space, Equals sign or better
675 if( sbiDecode
>= EQUALS_SIGN_ENC
)
677 b4
[ b4Posn
++ ] = sbiCrop
;
680 outBuffPosn
+= decode4to3( b4
, 0, outBuff
, outBuffPosn
);
683 // If that was the equals sign, break out of 'for' loop
684 if( sbiCrop
== EQUALS_SIGN
)
686 } // end if: quartet built
688 } // end if: equals sign or better
690 } // end if: white space, equals sign or better
693 System
.err
.println( "Bad Base64 input character at " + i
+ ": " + source
[i
] + "(decimal)" );
696 } // each input character
698 byte[] out
= new byte[ outBuffPosn
];
699 System
.arraycopy( outBuff
, 0, out
, 0, outBuffPosn
);
707 * Decodes data from Base64 notation, automatically
708 * detecting gzip-compressed data and decompressing it.
710 * @param s the string to decode
711 * @return the decoded data
714 public static byte[] decode( String s
)
719 bytes
= s
.getBytes( PREFERRED_ENCODING
);
721 catch( java
.io
.UnsupportedEncodingException uee
)
723 bytes
= s
.getBytes();
728 bytes
= decode( bytes
, 0, bytes
.length
);
731 // Check to see if it's gzip-compressed
732 // GZIP Magic Two-Byte Number: 0x8b1f (35615)
733 if( bytes
!= null && bytes
.length
>= 4 )
736 int head
= (bytes
[0] & 0xff) | ((bytes
[1] << 8) & 0xff00);
737 if( java
.util
.zip
.GZIPInputStream
.GZIP_MAGIC
== head
)
739 java
.io
.ByteArrayInputStream bais
= null;
740 java
.util
.zip
.GZIPInputStream gzis
= null;
741 java
.io
.ByteArrayOutputStream baos
= null;
742 byte[] buffer
= new byte[2048];
747 baos
= new java
.io
.ByteArrayOutputStream();
748 bais
= new java
.io
.ByteArrayInputStream( bytes
);
749 gzis
= new java
.util
.zip
.GZIPInputStream( bais
);
751 while( ( length
= gzis
.read( buffer
) ) >= 0 )
753 baos
.write(buffer
,0,length
);
754 } // end while: reading input
756 // No error? Get new bytes.
757 bytes
= baos
.toByteArray();
760 catch( java
.io
.IOException e
)
762 // Just return originally-decoded bytes
766 try{ baos
.close(); } catch( Exception e
){}
767 try{ gzis
.close(); } catch( Exception e
){}
768 try{ bais
.close(); } catch( Exception e
){}
772 } // end if: bytes.length >= 2
781 * Attempts to decode Base64 data and deserialize a Java
782 * Object within. Returns <tt>null</tt> if there was an error.
784 * @param encodedObject The Base64 data to decode
785 * @return The decoded and deserialized object
788 public static Object
decodeToObject( String encodedObject
)
790 // Decode and gunzip if necessary
791 byte[] objBytes
= decode( encodedObject
);
793 java
.io
.ByteArrayInputStream bais
= null;
794 java
.io
.ObjectInputStream ois
= null;
799 bais
= new java
.io
.ByteArrayInputStream( objBytes
);
800 ois
= new java
.io
.ObjectInputStream( bais
);
802 obj
= ois
.readObject();
804 catch( java
.io
.IOException e
)
809 catch( java
.lang
.ClassNotFoundException e
)
816 try{ bais
.close(); } catch( Exception e
){}
817 try{ ois
.close(); } catch( Exception e
){}
821 } // end decodeObject
826 * Convenience method for encoding data to a file.
828 * @param dataToEncode byte array of data to encode in base64 form
829 * @param filename Filename for saving encoded data
830 * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
834 public static boolean encodeToFile( byte[] dataToEncode
, String filename
)
836 boolean success
= false;
837 Base64
.OutputStream bos
= null;
840 bos
= new Base64
.OutputStream(
841 new java
.io
.FileOutputStream( filename
), Base64
.ENCODE
);
842 bos
.write( dataToEncode
);
845 catch( java
.io
.IOException e
)
849 } // end catch: IOException
852 try{ bos
.close(); } catch( Exception e
){}
856 } // end encodeToFile
860 * Convenience method for decoding data to a file.
862 * @param dataToDecode Base64-encoded data as a string
863 * @param filename Filename for saving decoded data
864 * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
868 public static boolean decodeToFile( String dataToDecode
, String filename
)
870 boolean success
= false;
871 Base64
.OutputStream bos
= null;
874 bos
= new Base64
.OutputStream(
875 new java
.io
.FileOutputStream( filename
), Base64
.DECODE
);
876 bos
.write( dataToDecode
.getBytes( PREFERRED_ENCODING
) );
879 catch( java
.io
.IOException e
)
882 } // end catch: IOException
885 try{ bos
.close(); } catch( Exception e
){}
889 } // end decodeToFile
895 * Convenience method for reading a base64-encoded
896 * file and decoding it.
898 * @param filename Filename for reading encoded data
899 * @return decoded byte array or null if unsuccessful
903 public static byte[] decodeFromFile( String filename
)
905 byte[] decodedData
= null;
906 Base64
.InputStream bis
= null;
909 // Set up some useful variables
910 java
.io
.File file
= new java
.io
.File( filename
);
911 byte[] buffer
= null;
915 // Check for size of file
916 if( file
.length() > Integer
.MAX_VALUE
)
918 System
.err
.println( "File is too big for this convenience method (" + file
.length() + " bytes)." );
920 } // end if: file too big for int index
921 buffer
= new byte[ (int)file
.length() ];
924 bis
= new Base64
.InputStream(
925 new java
.io
.BufferedInputStream(
926 new java
.io
.FileInputStream( file
) ), Base64
.DECODE
);
929 while( ( numBytes
= bis
.read( buffer
, length
, 4096 ) ) >= 0 )
932 // Save in a variable to return
933 decodedData
= new byte[ length
];
934 System
.arraycopy( buffer
, 0, decodedData
, 0, length
);
937 catch( java
.io
.IOException e
)
939 System
.err
.println( "Error decoding from file " + filename
);
940 } // end catch: IOException
943 try{ bis
.close(); } catch( Exception e
) {}
947 } // end decodeFromFile
952 * Convenience method for reading a binary file
953 * and base64-encoding it.
955 * @param filename Filename for reading binary data
956 * @return base64-encoded string or null if unsuccessful
960 public static String
encodeFromFile( String filename
)
962 String encodedData
= null;
963 Base64
.InputStream bis
= null;
966 // Set up some useful variables
967 java
.io
.File file
= new java
.io
.File( filename
);
968 byte[] buffer
= new byte[ (int)(file
.length() * 1.4) ];
973 bis
= new Base64
.InputStream(
974 new java
.io
.BufferedInputStream(
975 new java
.io
.FileInputStream( file
) ), Base64
.ENCODE
);
978 while( ( numBytes
= bis
.read( buffer
, length
, 4096 ) ) >= 0 )
981 // Save in a variable to return
982 encodedData
= new String( buffer
, 0, length
, Base64
.PREFERRED_ENCODING
);
985 catch( java
.io
.IOException e
)
987 System
.err
.println( "Error encoding from file " + filename
);
988 } // end catch: IOException
991 try{ bis
.close(); } catch( Exception e
) {}
995 } // end encodeFromFile
1000 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
1005 * A {@link Base64.InputStream} will read data from another
1006 * <tt>java.io.InputStream</tt>, given in the constructor,
1007 * and encode/decode to/from Base64 notation on the fly.
1012 public static class InputStream
extends java
.io
.FilterInputStream
1014 private boolean encode
; // Encoding or decoding
1015 private int position
; // Current position in the buffer
1016 private byte[] buffer
; // Small buffer holding converted data
1017 private int bufferLength
; // Length of buffer (3 or 4)
1018 private int numSigBytes
; // Number of meaningful bytes in the buffer
1019 private int lineLength
;
1020 private boolean breakLines
; // Break lines at less than 80 characters
1024 * Constructs a {@link Base64.InputStream} in DECODE mode.
1026 * @param in the <tt>java.io.InputStream</tt> from which to read data.
1029 public InputStream( java
.io
.InputStream in
)
1032 } // end constructor
1036 * Constructs a {@link Base64.InputStream} in
1037 * either ENCODE or DECODE mode.
1039 * Valid options:<pre>
1040 * ENCODE or DECODE: Encode or Decode as data is read.
1041 * DONT_BREAK_LINES: don't break lines at 76 characters
1042 * (only meaningful when encoding)
1043 * <i>Note: Technically, this makes your encoding non-compliant.</i>
1046 * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1049 * @param in the <tt>java.io.InputStream</tt> from which to read data.
1050 * @param options Specified options
1051 * @see Base64#ENCODE
1052 * @see Base64#DECODE
1053 * @see Base64#DONT_BREAK_LINES
1056 public InputStream( java
.io
.InputStream in
, int options
)
1059 this.breakLines
= (options
& DONT_BREAK_LINES
) != DONT_BREAK_LINES
;
1060 this.encode
= (options
& ENCODE
) == ENCODE
;
1061 this.bufferLength
= encode ?
4 : 3;
1062 this.buffer
= new byte[ bufferLength
];
1064 this.lineLength
= 0;
1065 } // end constructor
1068 * Reads enough of the input stream to convert
1069 * to/from Base64 and returns the next byte.
1074 public int read() throws java
.io
.IOException
1076 // Do we need to get data?
1081 byte[] b3
= new byte[3];
1082 int numBinaryBytes
= 0;
1083 for( int i
= 0; i
< 3; i
++ )
1089 // If end of stream, b is -1.
1094 } // end if: not end of stream
1097 catch( java
.io
.IOException e
)
1099 // Only a problem if we got no data at all.
1104 } // end for: each needed input byte
1106 if( numBinaryBytes
> 0 )
1108 encode3to4( b3
, 0, numBinaryBytes
, buffer
, 0 );
1111 } // end if: got data
1116 } // end if: encoding
1121 byte[] b4
= new byte[4];
1123 for( i
= 0; i
< 4; i
++ )
1125 // Read four "meaningful" bytes:
1127 do{ b
= in
.read(); }
1128 while( b
>= 0 && DECODABET
[ b
& 0x7f ] <= WHITE_SPACE_ENC
);
1131 break; // Reads a -1 if end of stream
1134 } // end for: each needed input byte
1138 numSigBytes
= decode4to3( b4
, 0, buffer
, 0 );
1140 } // end if: got four characters
1143 } // end else if: also padded correctly
1146 // Must have broken out from above.
1147 throw new java
.io
.IOException( "Improperly padded Base64 input." );
1150 } // end else: decode
1151 } // end else: get data
1156 // End of relevant data?
1157 if( /*!encode &&*/ position
>= numSigBytes
)
1160 if( encode
&& breakLines
&& lineLength
>= MAX_LINE_LENGTH
)
1167 lineLength
++; // This isn't important when decoding
1168 // but throwing an extra "if" seems
1169 // just as wasteful.
1171 int b
= buffer
[ position
++ ];
1173 if( position
>= bufferLength
)
1176 return b
& 0xFF; // This is how you "cast" a byte that's
1177 // intended to be unsigned.
1179 } // end if: position >= 0
1184 // When JDK1.4 is more accepted, use an assertion here.
1185 throw new java
.io
.IOException( "Error in Base64 code reading stream." );
1191 * Calls {@link #read()} repeatedly until the end of stream
1192 * is reached or <var>len</var> bytes are read.
1193 * Returns number of bytes read into array or -1 if
1194 * end of stream is encountered.
1196 * @param dest array to hold values
1197 * @param off offset for array
1198 * @param len max number of bytes to read into array
1199 * @return bytes read into array or -1 if end of stream is encountered.
1202 public int read( byte[] dest
, int off
, int len
) throws java
.io
.IOException
1206 for( i
= 0; i
< len
; i
++ )
1210 //if( b < 0 && i == 0 )
1214 dest
[off
+ i
] = (byte)b
;
1218 break; // Out of 'for' loop
1219 } // end for: each byte read
1223 } // end inner class InputStream
1230 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1235 * A {@link Base64.OutputStream} will write data to another
1236 * <tt>java.io.OutputStream</tt>, given in the constructor,
1237 * and encode/decode to/from Base64 notation on the fly.
1242 public static class OutputStream
extends java
.io
.FilterOutputStream
1244 private boolean encode
;
1245 private int position
;
1246 private byte[] buffer
;
1247 private int bufferLength
;
1248 private int lineLength
;
1249 private boolean breakLines
;
1250 private byte[] b4
; // Scratch used in a few places
1251 private boolean suspendEncoding
;
1254 * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1256 * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1259 public OutputStream( java
.io
.OutputStream out
)
1261 this( out
, ENCODE
);
1262 } // end constructor
1266 * Constructs a {@link Base64.OutputStream} in
1267 * either ENCODE or DECODE mode.
1269 * Valid options:<pre>
1270 * ENCODE or DECODE: Encode or Decode as data is read.
1271 * DONT_BREAK_LINES: don't break lines at 76 characters
1272 * (only meaningful when encoding)
1273 * <i>Note: Technically, this makes your encoding non-compliant.</i>
1276 * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1278 * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1279 * @param options Specified options.
1280 * @see Base64#ENCODE
1281 * @see Base64#DECODE
1282 * @see Base64#DONT_BREAK_LINES
1285 public OutputStream( java
.io
.OutputStream out
, int options
)
1288 this.breakLines
= (options
& DONT_BREAK_LINES
) != DONT_BREAK_LINES
;
1289 this.encode
= (options
& ENCODE
) == ENCODE
;
1290 this.bufferLength
= encode ?
3 : 4;
1291 this.buffer
= new byte[ bufferLength
];
1293 this.lineLength
= 0;
1294 this.suspendEncoding
= false;
1295 this.b4
= new byte[4];
1296 } // end constructor
1300 * Writes the byte to the output stream after
1301 * converting to/from Base64 notation.
1302 * When encoding, bytes are buffered three
1303 * at a time before the output stream actually
1304 * gets a write() call.
1305 * When decoding, bytes are buffered four
1308 * @param theByte the byte to write
1311 public void write(int theByte
) throws java
.io
.IOException
1313 // Encoding suspended?
1314 if( suspendEncoding
)
1316 super.out
.write( theByte
);
1318 } // end if: suspended
1323 buffer
[ position
++ ] = (byte)theByte
;
1324 if( position
>= bufferLength
) // Enough to encode.
1326 out
.write( encode3to4( b4
, buffer
, bufferLength
) );
1329 if( breakLines
&& lineLength
>= MAX_LINE_LENGTH
)
1331 out
.write( NEW_LINE
);
1333 } // end if: end of line
1336 } // end if: enough to output
1337 } // end if: encoding
1342 // Meaningful Base64 character?
1343 if( DECODABET
[ theByte
& 0x7f ] > WHITE_SPACE_ENC
)
1345 buffer
[ position
++ ] = (byte)theByte
;
1346 if( position
>= bufferLength
) // Enough to output.
1348 int len
= Base64
.decode4to3( buffer
, 0, b4
, 0 );
1349 out
.write( b4
, 0, len
);
1350 //out.write( Base64.decode4to3( buffer ) );
1352 } // end if: enough to output
1353 } // end if: meaningful base64 character
1354 else if( DECODABET
[ theByte
& 0x7f ] != WHITE_SPACE_ENC
)
1356 throw new java
.io
.IOException( "Invalid character in Base64 data." );
1357 } // end else: not white space either
1358 } // end else: decoding
1364 * Calls {@link #write(int)} repeatedly until <var>len</var>
1365 * bytes are written.
1367 * @param theBytes array from which to read bytes
1368 * @param off offset for array
1369 * @param len max number of bytes to read into array
1372 public void write( byte[] theBytes
, int off
, int len
) throws java
.io
.IOException
1374 // Encoding suspended?
1375 if( suspendEncoding
)
1377 super.out
.write( theBytes
, off
, len
);
1379 } // end if: suspended
1381 for( int i
= 0; i
< len
; i
++ )
1383 write( theBytes
[ off
+ i
] );
1384 } // end for: each byte written
1391 * Method added by PHIL. [Thanks, PHIL. -Rob]
1392 * This pads the buffer without closing the stream.
1393 * @throws java.io.IOException input was not properly padded.
1395 public void flushBase64() throws java
.io
.IOException
1401 out
.write( encode3to4( b4
, buffer
, position
) );
1403 } // end if: encoding
1406 throw new java
.io
.IOException( "Base64 input not properly padded." );
1407 } // end else: decoding
1408 } // end if: buffer partially full
1414 * Flushes and closes (I think, in the superclass) the stream.
1418 public void close() throws java
.io
.IOException
1420 // 1. Ensure that pending characters are written
1423 // 2. Actually close the stream
1424 // Base class both flushes and closes.
1434 * Suspends encoding of the stream.
1435 * May be helpful if you need to embed a piece of
1436 * base640-encoded data in a stream.
1438 * @throws java.io.IOException input was not properly padded.
1441 public void suspendEncoding() throws java
.io
.IOException
1444 this.suspendEncoding
= true;
1445 } // end suspendEncoding
1449 * Resumes encoding of the stream.
1450 * May be helpful if you need to embed a piece of
1451 * base640-encoded data in a stream.
1455 public void resumeEncoding()
1457 this.suspendEncoding
= false;
1458 } // end resumeEncoding
1462 } // end inner class OutputStream
1465 } // end class Base64