Add a few simple merge test cases
[egit/charleso.git] / org.spearce.jgit / src / org / spearce / jgit / util / Base64.java
blobd81867b8281e57c7b6e988f4cbadcfb073017bd1
1 //
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/
6 //
8 package org.spearce.jgit.util;
10 import java.io.Closeable;
11 import java.io.IOException;
14 /**
15 * Encodes and decodes to and from Base64 notation.
17 * <p>
18 * Change Log:
19 * </p>
20 * <ul>
21 * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
22 * some convenience methods for reading and writing to and from files.</li>
23 * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
24 * with other encodings (like EBCDIC).</li>
25 * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
26 * encoded data was a single byte.</li>
27 * <li>v2.0 - I got rid of methods that used booleans to set options.
28 * Now everything is more consolidated and cleaner. The code now detects
29 * when data that's being decoded is gzip-compressed and will decompress it
30 * automatically. Generally things are cleaner. You'll probably have to
31 * change some method calls that you were making to support the new
32 * options format (<tt>int</tt>s that you "OR" together).</li>
33 * <li>v1.5.1 - Fixed bug when decompressing and decoding to a
34 * byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
35 * Added the ability to "suspend" encoding in the Output Stream so
36 * you can turn on and off the encoding if you need to embed base64
37 * data in an otherwise "normal" stream (like an XML file).</li>
38 * <li>v1.5 - Output stream passes on flush() command but doesn't do anything itself.
39 * This helps when using GZIP streams.
40 * Added the ability to GZip-compress objects before encoding them.</li>
41 * <li>v1.4 - Added helper methods to read/write files.</li>
42 * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
43 * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
44 * where last buffer being read, if not completely full, was not returned.</li>
45 * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
46 * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
47 * </ul>
49 * <p>
50 * I am placing this code in the Public Domain. Do with it as you will.
51 * This software comes with no guarantees or warranties but with
52 * plenty of well-wishing instead!
53 * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
54 * periodically to check for updates or to contribute improvements.
55 * </p>
57 * @author Robert Harder
58 * @author rob@iharder.net
59 * @version 2.1
61 public class Base64
64 /* ******** P U B L I C F I E L D S ******** */
67 /** No options specified. Value is zero. */
68 public final static int NO_OPTIONS = 0;
70 /** Specify encoding. */
71 public final static int ENCODE = 1;
74 /** Specify decoding. */
75 public final static int DECODE = 0;
78 /** Specify that data should be gzip-compressed. */
79 public final static int GZIP = 2;
82 /** Don't break lines when encoding (violates strict Base64 specification) */
83 public final static int DONT_BREAK_LINES = 8;
86 /* ******** P R I V A T E F I E L D S ******** */
89 /** Maximum line length (76) of Base64 output. */
90 private final static int MAX_LINE_LENGTH = 76;
93 /** The equals sign (=) as a byte. */
94 private final static byte EQUALS_SIGN = (byte)'=';
97 /** The new line character (\n) as a byte. */
98 private final static byte NEW_LINE = (byte)'\n';
101 /** Preferred encoding. */
102 private final static String PREFERRED_ENCODING = "UTF-8";
105 /** The 64 valid Base64 values. */
106 private final static byte[] ALPHABET;
107 private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */
109 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
110 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
111 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
112 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
113 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
114 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
115 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
116 (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
117 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
118 (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
121 /** Determine which ALPHABET to use. */
122 static
124 byte[] __bytes;
127 __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING );
128 } // end try
129 catch (java.io.UnsupportedEncodingException use)
131 __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
132 } // end catch
133 ALPHABET = __bytes;
134 } // end static
138 * Translates a Base64 value to either its 6-bit reconstruction value
139 * or a negative number indicating some other meaning.
141 private final static byte[] DECODABET =
143 -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
144 -5,-5, // Whitespace: Tab and Linefeed
145 -9,-9, // Decimal 11 - 12
146 -5, // Whitespace: Carriage Return
147 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
148 -9,-9,-9,-9,-9, // Decimal 27 - 31
149 -5, // Whitespace: Space
150 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
151 62, // Plus sign at decimal 43
152 -9,-9,-9, // Decimal 44 - 46
153 63, // Slash at decimal 47
154 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
155 -9,-9,-9, // Decimal 58 - 60
156 -1, // Equals sign at decimal 61
157 -9,-9,-9, // Decimal 62 - 64
158 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
159 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
160 -9,-9,-9,-9,-9,-9, // Decimal 91 - 96
161 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
162 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
163 -9,-9,-9,-9 // Decimal 123 - 126
164 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
165 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
166 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
167 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
168 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
169 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
170 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
171 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
172 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
173 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
176 // I think I end up not using the BAD_ENCODING indicator.
177 //private final static byte BAD_ENCODING = -9; // Indicates error in encoding
178 private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
179 private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
181 private static void closeStream(Closeable stream) {
182 if (stream != null) {
183 try {
184 stream.close();
185 } catch (IOException e) {
186 e.printStackTrace();
191 /** Defeats instantiation. */
192 private Base64() {
193 //suppress empty block warning
196 /* ******** E N C O D I N G M E T H O D S ******** */
200 * Encodes up to the first three bytes of array <var>threeBytes</var>
201 * and returns a four-byte array in Base64 notation.
202 * The actual number of significant bytes in your array is
203 * given by <var>numSigBytes</var>.
204 * The array <var>threeBytes</var> needs only be as big as
205 * <var>numSigBytes</var>.
206 * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
208 * @param b4 A reusable byte array to reduce array instantiation
209 * @param threeBytes the array to convert
210 * @param numSigBytes the number of significant bytes in your array
211 * @return four byte array in Base64 notation.
212 * @since 1.5.1
214 private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes )
216 encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
217 return b4;
218 } // end encode3to4
222 * Encodes up to three bytes of the array <var>source</var>
223 * and writes the resulting four Base64 bytes to <var>destination</var>.
224 * The source and destination arrays can be manipulated
225 * anywhere along their length by specifying
226 * <var>srcOffset</var> and <var>destOffset</var>.
227 * This method does not check to make sure your arrays
228 * are large enough to accommodate <var>srcOffset</var> + 3 for
229 * the <var>source</var> array or <var>destOffset</var> + 4 for
230 * the <var>destination</var> array.
231 * The actual number of significant bytes in your array is
232 * given by <var>numSigBytes</var>.
234 * @param source the array to convert
235 * @param srcOffset the index where conversion begins
236 * @param numSigBytes the number of significant bytes in your array
237 * @param destination the array to hold the conversion
238 * @param destOffset the index where output will be put
239 * @return the <var>destination</var> array
240 * @since 1.3
242 private static byte[] encode3to4(
243 byte[] source, int srcOffset, int numSigBytes,
244 byte[] destination, int destOffset )
246 // 1 2 3
247 // 01234567890123456789012345678901 Bit position
248 // --------000000001111111122222222 Array position from threeBytes
249 // --------| || || || | Six bit groups to index ALPHABET
250 // >>18 >>12 >> 6 >> 0 Right shift necessary
251 // 0x3f 0x3f 0x3f Additional AND
253 // Create buffer with zero-padding if there are only one or two
254 // significant bytes passed in the array.
255 // We have to shift left 24 in order to flush out the 1's that appear
256 // when Java treats a value as negative that is cast from a byte to an int.
257 int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
258 | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
259 | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
261 switch( numSigBytes )
263 case 3:
264 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
265 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
266 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
267 destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
268 return destination;
270 case 2:
271 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
272 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
273 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
274 destination[ destOffset + 3 ] = EQUALS_SIGN;
275 return destination;
277 case 1:
278 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
279 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
280 destination[ destOffset + 2 ] = EQUALS_SIGN;
281 destination[ destOffset + 3 ] = EQUALS_SIGN;
282 return destination;
284 default:
285 return destination;
286 } // end switch
287 } // end encode3to4
292 * Serializes an object and returns the Base64-encoded
293 * version of that serialized object. If the object
294 * cannot be serialized or there is another error,
295 * the method will return <tt>null</tt>.
296 * The object is not GZip-compressed before being encoded.
298 * @param serializableObject The object to encode
299 * @return The Base64-encoded object
300 * @since 1.4
302 public static String encodeObject( java.io.Serializable serializableObject )
304 return encodeObject( serializableObject, NO_OPTIONS );
305 } // end encodeObject
310 * Serializes an object and returns the Base64-encoded
311 * version of that serialized object. If the object
312 * cannot be serialized or there is another error,
313 * the method will return <tt>null</tt>.
314 * <p>
315 * Valid options:<pre>
316 * GZIP: gzip-compresses object before encoding it.
317 * DONT_BREAK_LINES: don't break lines at 76 characters
318 * <i>Note: Technically, this makes your encoding non-compliant.</i>
319 * </pre>
320 * <p>
321 * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
322 * <p>
323 * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
325 * @param serializableObject The object to encode
326 * @param options Specified options
327 * @return The Base64-encoded object
328 * @see Base64#GZIP
329 * @see Base64#DONT_BREAK_LINES
330 * @since 2.0
332 public static String encodeObject( java.io.Serializable serializableObject, int options )
334 // Streams
335 java.io.ByteArrayOutputStream baos = null;
336 java.io.OutputStream b64os = null;
337 java.io.ObjectOutputStream oos = null;
338 java.util.zip.GZIPOutputStream gzos = null;
340 // Isolate options
341 int gzip = (options & GZIP);
342 int dontBreakLines = (options & DONT_BREAK_LINES);
346 // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
347 baos = new java.io.ByteArrayOutputStream();
348 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
350 // GZip?
351 if( gzip == GZIP )
353 gzos = new java.util.zip.GZIPOutputStream( b64os );
354 oos = new java.io.ObjectOutputStream( gzos );
355 } // end if: gzip
356 else
357 oos = new java.io.ObjectOutputStream( b64os );
359 oos.writeObject( serializableObject );
360 } // end try
361 catch( java.io.IOException e )
363 e.printStackTrace();
364 return null;
365 } // end catch
366 finally
368 closeStream(oos);
369 closeStream(gzos);
370 closeStream(b64os);
371 closeStream(baos);
372 } // end finally
374 // Return value according to relevant encoding.
377 return new String( baos.toByteArray(), PREFERRED_ENCODING );
378 } // end try
379 catch (java.io.UnsupportedEncodingException uue)
381 return new String( baos.toByteArray() );
382 } // end catch
384 } // end encode
389 * Encodes a byte array into Base64 notation.
390 * Does not GZip-compress data.
392 * @param source The data to convert
393 * @return encoded base64 representation of source.
394 * @since 1.4
396 public static String encodeBytes( byte[] source )
398 return encodeBytes( source, 0, source.length, NO_OPTIONS );
399 } // end encodeBytes
404 * Encodes a byte array into Base64 notation.
405 * <p>
406 * Valid options:<pre>
407 * GZIP: gzip-compresses object before encoding it.
408 * DONT_BREAK_LINES: don't break lines at 76 characters
409 * <i>Note: Technically, this makes your encoding non-compliant.</i>
410 * </pre>
411 * <p>
412 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
413 * <p>
414 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
417 * @param source The data to convert
418 * @param options Specified options
419 * @return encoded base64 representation of source.
420 * @see Base64#GZIP
421 * @see Base64#DONT_BREAK_LINES
422 * @since 2.0
424 public static String encodeBytes( byte[] source, int options )
426 return encodeBytes( source, 0, source.length, options );
427 } // end encodeBytes
431 * Encodes a byte array into Base64 notation.
432 * Does not GZip-compress data.
434 * @param source The data to convert
435 * @param off Offset in array where conversion should begin
436 * @param len Length of data to convert
437 * @return encoded base64 representation of source.
438 * @since 1.4
440 public static String encodeBytes( byte[] source, int off, int len )
442 return encodeBytes( source, off, len, NO_OPTIONS );
443 } // end encodeBytes
448 * Encodes a byte array into Base64 notation.
449 * <p>
450 * Valid options:<pre>
451 * GZIP: gzip-compresses object before encoding it.
452 * DONT_BREAK_LINES: don't break lines at 76 characters
453 * <i>Note: Technically, this makes your encoding non-compliant.</i>
454 * </pre>
455 * <p>
456 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
457 * <p>
458 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
461 * @param source The data to convert
462 * @param off Offset in array where conversion should begin
463 * @param len Length of data to convert
464 * @param options Specified options
465 * @return encoded base64 representation of source.
466 * @see Base64#GZIP
467 * @see Base64#DONT_BREAK_LINES
468 * @since 2.0
470 public static String encodeBytes( byte[] source, int off, int len, int options )
472 // Isolate options
473 int dontBreakLines = ( options & DONT_BREAK_LINES );
474 int gzip = ( options & GZIP );
476 // Compress?
477 if( gzip == GZIP )
479 java.io.ByteArrayOutputStream baos = null;
480 java.util.zip.GZIPOutputStream gzos = null;
481 Base64.OutputStream b64os = null;
486 // GZip -> Base64 -> ByteArray
487 baos = new java.io.ByteArrayOutputStream();
488 b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
489 gzos = new java.util.zip.GZIPOutputStream( b64os );
491 gzos.write( source, off, len );
492 gzos.close();
493 } // end try
494 catch( java.io.IOException e )
496 e.printStackTrace();
497 return null;
498 } // end catch
499 finally
501 closeStream(gzos);
502 closeStream(b64os);
503 closeStream(baos);
504 } // end finally
506 // Return value according to relevant encoding.
509 return new String( baos.toByteArray(), PREFERRED_ENCODING );
510 } // end try
511 catch (java.io.UnsupportedEncodingException uue)
513 return new String( baos.toByteArray() );
514 } // end catch
515 } // end if: compress
517 // Else, don't compress. Better not to use streams at all then.
518 else
520 // Convert option to boolean in way that code likes it.
521 boolean breakLines = dontBreakLines == 0;
523 int len43 = len * 4 / 3;
524 byte[] outBuff = new byte[ ( len43 ) // Main 4:3
525 + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
526 + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
527 int d = 0;
528 int e = 0;
529 int len2 = len - 2;
530 int lineLength = 0;
531 for( ; d < len2; d+=3, e+=4 )
533 encode3to4( source, d+off, 3, outBuff, e );
535 lineLength += 4;
536 if( breakLines && lineLength == MAX_LINE_LENGTH )
538 outBuff[e+4] = NEW_LINE;
539 e++;
540 lineLength = 0;
541 } // end if: end of line
542 } // end for: each piece of array
544 if( d < len )
546 encode3to4( source, d+off, len - d, outBuff, e );
547 e += 4;
548 } // end if: some padding needed
551 // Return value according to relevant encoding.
554 return new String( outBuff, 0, e, PREFERRED_ENCODING );
555 } // end try
556 catch (java.io.UnsupportedEncodingException uue)
558 return new String( outBuff, 0, e );
559 } // end catch
561 } // end else: don't compress
563 } // end encodeBytes
569 /* ******** D E C O D I N G M E T H O D S ******** */
573 * Decodes four bytes from array <var>source</var>
574 * and writes the resulting bytes (up to three of them)
575 * to <var>destination</var>.
576 * The source and destination arrays can be manipulated
577 * anywhere along their length by specifying
578 * <var>srcOffset</var> and <var>destOffset</var>.
579 * This method does not check to make sure your arrays
580 * are large enough to accommodate <var>srcOffset</var> + 4 for
581 * the <var>source</var> array or <var>destOffset</var> + 3 for
582 * the <var>destination</var> array.
583 * This method returns the actual number of bytes that
584 * were converted from the Base64 encoding.
587 * @param source the array to convert
588 * @param srcOffset the index where conversion begins
589 * @param destination the array to hold the conversion
590 * @param destOffset the index where output will be put
591 * @return the number of decoded bytes converted
592 * @since 1.3
594 private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
596 // Example: Dk==
597 if( source[ srcOffset + 2] == EQUALS_SIGN )
599 // Two ways to do the same thing. Don't know which way I like best.
600 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
601 // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
602 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
603 | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
605 destination[ destOffset ] = (byte)( outBuff >>> 16 );
606 return 1;
609 // Example: DkL=
610 else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
612 // Two ways to do the same thing. Don't know which way I like best.
613 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
614 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
615 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
616 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
617 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
618 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 );
620 destination[ destOffset ] = (byte)( outBuff >>> 16 );
621 destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 );
622 return 2;
625 // Example: DkLE
626 else
628 try{
629 // Two ways to do the same thing. Don't know which way I like best.
630 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
631 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
632 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
633 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
634 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
635 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
636 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6)
637 | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
640 destination[ destOffset ] = (byte)( outBuff >> 16 );
641 destination[ destOffset + 1 ] = (byte)( outBuff >> 8 );
642 destination[ destOffset + 2 ] = (byte)( outBuff );
644 return 3;
645 }catch( Exception e){
646 System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) );
647 System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) );
648 System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) );
649 System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) );
650 return -1;
651 } //e nd catch
653 } // end decodeToBytes
659 * Very low-level access to decoding ASCII characters in
660 * the form of a byte array. Does not support automatically
661 * gunzipping or any other "fancy" features.
663 * @param source The Base64 encoded data
664 * @param off The offset of where to begin decoding
665 * @param len The length of characters to decode
666 * @return decoded data
667 * @since 1.3
669 public static byte[] decode( byte[] source, int off, int len )
671 int len34 = len * 3 / 4;
672 byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
673 int outBuffPosn = 0;
675 byte[] b4 = new byte[4];
676 int b4Posn = 0;
677 int i = 0;
678 byte sbiCrop = 0;
679 byte sbiDecode = 0;
680 for( i = off; i < off+len; i++ )
682 sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
683 sbiDecode = DECODABET[ sbiCrop ];
685 if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
687 if( sbiDecode >= EQUALS_SIGN_ENC )
689 b4[ b4Posn++ ] = sbiCrop;
690 if( b4Posn > 3 )
692 outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
693 b4Posn = 0;
695 // If that was the equals sign, break out of 'for' loop
696 if( sbiCrop == EQUALS_SIGN )
697 break;
698 } // end if: quartet built
700 } // end if: equals sign or better
702 } // end if: white space, equals sign or better
703 else
705 System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
706 return null;
707 } // end else:
708 } // each input character
710 byte[] out = new byte[ outBuffPosn ];
711 System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
712 return out;
713 } // end decode
719 * Decodes data from Base64 notation, automatically
720 * detecting gzip-compressed data and decompressing it.
722 * @param s the string to decode
723 * @return the decoded data
724 * @since 1.4
726 public static byte[] decode( String s )
728 byte[] bytes;
731 bytes = s.getBytes( PREFERRED_ENCODING );
732 } // end try
733 catch( java.io.UnsupportedEncodingException uee )
735 bytes = s.getBytes();
736 } // end catch
737 //</change>
739 // Decode
740 bytes = decode( bytes, 0, bytes.length );
743 // Check to see if it's gzip-compressed
744 // GZIP Magic Two-Byte Number: 0x8b1f (35615)
745 if( bytes != null && bytes.length >= 4 )
748 int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
749 if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head )
751 java.io.ByteArrayInputStream bais = null;
752 java.util.zip.GZIPInputStream gzis = null;
753 java.io.ByteArrayOutputStream baos = null;
754 byte[] buffer = new byte[2048];
755 int length = 0;
759 baos = new java.io.ByteArrayOutputStream();
760 bais = new java.io.ByteArrayInputStream( bytes );
761 gzis = new java.util.zip.GZIPInputStream( bais );
763 while( ( length = gzis.read( buffer ) ) >= 0 )
765 baos.write(buffer,0,length);
766 } // end while: reading input
768 // No error? Get new bytes.
769 bytes = baos.toByteArray();
771 } // end try
772 catch( java.io.IOException e )
774 // Just return originally-decoded bytes
775 } // end catch
776 finally
778 closeStream(baos);
779 closeStream(gzis);
780 closeStream(bais);
781 } // end finally
783 } // end if: gzipped
784 } // end if: bytes.length >= 2
786 return bytes;
787 } // end decode
793 * Attempts to decode Base64 data and deserialize a Java
794 * Object within. Returns <tt>null</tt> if there was an error.
796 * @param encodedObject The Base64 data to decode
797 * @return The decoded and deserialized object
798 * @since 1.5
800 public static Object decodeToObject( String encodedObject )
802 // Decode and gunzip if necessary
803 byte[] objBytes = decode( encodedObject );
805 java.io.ByteArrayInputStream bais = null;
806 java.io.ObjectInputStream ois = null;
807 Object obj = null;
811 bais = new java.io.ByteArrayInputStream( objBytes );
812 ois = new java.io.ObjectInputStream( bais );
814 obj = ois.readObject();
815 } // end try
816 catch( java.io.IOException e )
818 e.printStackTrace();
819 } // end catch
820 catch( java.lang.ClassNotFoundException e )
822 e.printStackTrace();
823 } // end catch
824 finally
826 closeStream(bais);
827 closeStream(ois);
828 } // end finally
830 return obj;
831 } // end decodeObject
836 * Convenience method for encoding data to a file.
838 * @param dataToEncode byte array of data to encode in base64 form
839 * @param filename Filename for saving encoded data
840 * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
842 * @since 2.1
844 public static boolean encodeToFile( byte[] dataToEncode, String filename )
846 boolean success = false;
847 Base64.OutputStream bos = null;
850 bos = new Base64.OutputStream(
851 new java.io.FileOutputStream( filename ), Base64.ENCODE );
852 bos.write( dataToEncode );
853 success = true;
854 } // end try
855 catch( java.io.IOException e )
858 success = false;
859 } // end catch: IOException
860 finally
862 closeStream(bos);
863 } // end finally
865 return success;
866 } // end encodeToFile
870 * Convenience method for decoding data to a file.
872 * @param dataToDecode Base64-encoded data as a string
873 * @param filename Filename for saving decoded data
874 * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
876 * @since 2.1
878 public static boolean decodeToFile( String dataToDecode, String filename )
880 boolean success = false;
881 Base64.OutputStream bos = null;
884 bos = new Base64.OutputStream(
885 new java.io.FileOutputStream( filename ), Base64.DECODE );
886 bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
887 success = true;
888 } // end try
889 catch( java.io.IOException e )
891 success = false;
892 } // end catch: IOException
893 finally
895 closeStream(bos);
896 } // end finally
898 return success;
899 } // end decodeToFile
905 * Convenience method for reading a base64-encoded
906 * file and decoding it.
908 * @param filename Filename for reading encoded data
909 * @return decoded byte array or null if unsuccessful
911 * @since 2.1
913 public static byte[] decodeFromFile( String filename )
915 byte[] decodedData = null;
916 Base64.InputStream bis = null;
919 // Set up some useful variables
920 java.io.File file = new java.io.File( filename );
921 byte[] buffer = null;
922 int length = 0;
923 int numBytes = 0;
925 // Check for size of file
926 if( file.length() > Integer.MAX_VALUE )
928 System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." );
929 return null;
930 } // end if: file too big for int index
931 buffer = new byte[ (int)file.length() ];
933 // Open a stream
934 bis = new Base64.InputStream(
935 new java.io.BufferedInputStream(
936 new java.io.FileInputStream( file ) ), Base64.DECODE );
938 // Read until done
939 while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
940 length += numBytes;
942 // Save in a variable to return
943 decodedData = new byte[ length ];
944 System.arraycopy( buffer, 0, decodedData, 0, length );
946 } // end try
947 catch( java.io.IOException e )
949 System.err.println( "Error decoding from file " + filename );
950 } // end catch: IOException
951 finally
953 closeStream(bis);
954 } // end finally
956 return decodedData;
957 } // end decodeFromFile
962 * Convenience method for reading a binary file
963 * and base64-encoding it.
965 * @param filename Filename for reading binary data
966 * @return base64-encoded string or null if unsuccessful
968 * @since 2.1
970 public static String encodeFromFile( String filename )
972 String encodedData = null;
973 Base64.InputStream bis = null;
976 // Set up some useful variables
977 java.io.File file = new java.io.File( filename );
978 byte[] buffer = new byte[ (int)(file.length() * 1.4) ];
979 int length = 0;
980 int numBytes = 0;
982 // Open a stream
983 bis = new Base64.InputStream(
984 new java.io.BufferedInputStream(
985 new java.io.FileInputStream( file ) ), Base64.ENCODE );
987 // Read until done
988 while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
989 length += numBytes;
991 // Save in a variable to return
992 encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
994 } // end try
995 catch( java.io.IOException e )
997 System.err.println( "Error encoding from file " + filename );
998 } // end catch: IOException
999 finally
1001 closeStream(bis);
1002 } // end finally
1004 return encodedData;
1005 } // end encodeFromFile
1010 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
1015 * A {@link Base64.InputStream} will read data from another
1016 * <tt>java.io.InputStream</tt>, given in the constructor,
1017 * and encode/decode to/from Base64 notation on the fly.
1019 * @see Base64
1020 * @since 1.3
1022 public static class InputStream extends java.io.FilterInputStream
1024 private boolean encode; // Encoding or decoding
1025 private int position; // Current position in the buffer
1026 private byte[] buffer; // Small buffer holding converted data
1027 private int bufferLength; // Length of buffer (3 or 4)
1028 private int numSigBytes; // Number of meaningful bytes in the buffer
1029 private int lineLength;
1030 private boolean breakLines; // Break lines at less than 80 characters
1034 * Constructs a {@link Base64.InputStream} in DECODE mode.
1036 * @param in the <tt>java.io.InputStream</tt> from which to read data.
1037 * @since 1.3
1039 public InputStream( java.io.InputStream in )
1041 this( in, DECODE );
1042 } // end constructor
1046 * Constructs a {@link Base64.InputStream} in
1047 * either ENCODE or DECODE mode.
1048 * <p>
1049 * Valid options:<pre>
1050 * ENCODE or DECODE: Encode or Decode as data is read.
1051 * DONT_BREAK_LINES: don't break lines at 76 characters
1052 * (only meaningful when encoding)
1053 * <i>Note: Technically, this makes your encoding non-compliant.</i>
1054 * </pre>
1055 * <p>
1056 * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1059 * @param in the <tt>java.io.InputStream</tt> from which to read data.
1060 * @param options Specified options
1061 * @see Base64#ENCODE
1062 * @see Base64#DECODE
1063 * @see Base64#DONT_BREAK_LINES
1064 * @since 2.0
1066 public InputStream( java.io.InputStream in, int options )
1068 super( in );
1069 this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1070 this.encode = (options & ENCODE) == ENCODE;
1071 this.bufferLength = encode ? 4 : 3;
1072 this.buffer = new byte[ bufferLength ];
1073 this.position = -1;
1074 this.lineLength = 0;
1075 } // end constructor
1078 * Reads enough of the input stream to convert
1079 * to/from Base64 and returns the next byte.
1081 * @return next byte
1082 * @since 1.3
1084 public int read() throws java.io.IOException
1086 // Do we need to get data?
1087 if( position < 0 )
1089 if( encode )
1091 byte[] b3 = new byte[3];
1092 int numBinaryBytes = 0;
1093 for( int i = 0; i < 3; i++ )
1097 int b = in.read();
1099 // If end of stream, b is -1.
1100 if( b >= 0 )
1102 b3[i] = (byte)b;
1103 numBinaryBytes++;
1104 } // end if: not end of stream
1106 } // end try: read
1107 catch( java.io.IOException e )
1109 // Only a problem if we got no data at all.
1110 if( i == 0 )
1111 throw e;
1113 } // end catch
1114 } // end for: each needed input byte
1116 if( numBinaryBytes > 0 )
1118 encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
1119 position = 0;
1120 numSigBytes = 4;
1121 } // end if: got data
1122 else
1124 return -1;
1125 } // end else
1126 } // end if: encoding
1128 // Else decoding
1129 else
1131 byte[] b4 = new byte[4];
1132 int i = 0;
1133 for( i = 0; i < 4; i++ )
1135 // Read four "meaningful" bytes:
1136 int b = 0;
1137 do{ b = in.read(); }
1138 while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
1140 if( b < 0 )
1141 break; // Reads a -1 if end of stream
1143 b4[i] = (byte)b;
1144 } // end for: each needed input byte
1146 if( i == 4 )
1148 numSigBytes = decode4to3( b4, 0, buffer, 0 );
1149 position = 0;
1150 } // end if: got four characters
1151 else if( i == 0 ){
1152 return -1;
1153 } // end else if: also padded correctly
1154 else
1156 // Must have broken out from above.
1157 throw new java.io.IOException( "Improperly padded Base64 input." );
1158 } // end
1160 } // end else: decode
1161 } // end else: get data
1163 // Got data?
1164 if( position >= 0 )
1166 // End of relevant data?
1167 if( /*!encode &&*/ position >= numSigBytes )
1168 return -1;
1170 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
1172 lineLength = 0;
1173 return '\n';
1174 } // end if
1175 else
1177 lineLength++; // This isn't important when decoding
1178 // but throwing an extra "if" seems
1179 // just as wasteful.
1181 int b = buffer[ position++ ];
1183 if( position >= bufferLength )
1184 position = -1;
1186 return b & 0xFF; // This is how you "cast" a byte that's
1187 // intended to be unsigned.
1188 } // end else
1189 } // end if: position >= 0
1191 // Else error
1192 else
1194 // When JDK1.4 is more accepted, use an assertion here.
1195 throw new java.io.IOException( "Error in Base64 code reading stream." );
1196 } // end else
1197 } // end read
1201 * Calls {@link #read()} repeatedly until the end of stream
1202 * is reached or <var>len</var> bytes are read.
1203 * Returns number of bytes read into array or -1 if
1204 * end of stream is encountered.
1206 * @param dest array to hold values
1207 * @param off offset for array
1208 * @param len max number of bytes to read into array
1209 * @return bytes read into array or -1 if end of stream is encountered.
1210 * @since 1.3
1212 public int read( byte[] dest, int off, int len ) throws java.io.IOException
1214 int i;
1215 int b;
1216 for( i = 0; i < len; i++ )
1218 b = read();
1220 //if( b < 0 && i == 0 )
1221 // return -1;
1223 if( b >= 0 )
1224 dest[off + i] = (byte)b;
1225 else if( i == 0 )
1226 return -1;
1227 else
1228 break; // Out of 'for' loop
1229 } // end for: each byte read
1230 return i;
1231 } // end read
1233 } // end inner class InputStream
1240 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1245 * A {@link Base64.OutputStream} will write data to another
1246 * <tt>java.io.OutputStream</tt>, given in the constructor,
1247 * and encode/decode to/from Base64 notation on the fly.
1249 * @see Base64
1250 * @since 1.3
1252 public static class OutputStream extends java.io.FilterOutputStream
1254 private boolean encode;
1255 private int position;
1256 private byte[] buffer;
1257 private int bufferLength;
1258 private int lineLength;
1259 private boolean breakLines;
1260 private byte[] b4; // Scratch used in a few places
1261 private boolean suspendEncoding;
1264 * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1266 * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1267 * @since 1.3
1269 public OutputStream( java.io.OutputStream out )
1271 this( out, ENCODE );
1272 } // end constructor
1276 * Constructs a {@link Base64.OutputStream} in
1277 * either ENCODE or DECODE mode.
1278 * <p>
1279 * Valid options:<pre>
1280 * ENCODE or DECODE: Encode or Decode as data is read.
1281 * DONT_BREAK_LINES: don't break lines at 76 characters
1282 * (only meaningful when encoding)
1283 * <i>Note: Technically, this makes your encoding non-compliant.</i>
1284 * </pre>
1285 * <p>
1286 * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1288 * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1289 * @param options Specified options.
1290 * @see Base64#ENCODE
1291 * @see Base64#DECODE
1292 * @see Base64#DONT_BREAK_LINES
1293 * @since 1.3
1295 public OutputStream( java.io.OutputStream out, int options )
1297 super( out );
1298 this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1299 this.encode = (options & ENCODE) == ENCODE;
1300 this.bufferLength = encode ? 3 : 4;
1301 this.buffer = new byte[ bufferLength ];
1302 this.position = 0;
1303 this.lineLength = 0;
1304 this.suspendEncoding = false;
1305 this.b4 = new byte[4];
1306 } // end constructor
1310 * Writes the byte to the output stream after
1311 * converting to/from Base64 notation.
1312 * When encoding, bytes are buffered three
1313 * at a time before the output stream actually
1314 * gets a write() call.
1315 * When decoding, bytes are buffered four
1316 * at a time.
1318 * @param theByte the byte to write
1319 * @since 1.3
1321 public void write(int theByte) throws java.io.IOException
1323 // Encoding suspended?
1324 if( suspendEncoding )
1326 super.out.write( theByte );
1327 return;
1328 } // end if: suspended
1330 // Encode?
1331 if( encode )
1333 buffer[ position++ ] = (byte)theByte;
1334 if( position >= bufferLength ) // Enough to encode.
1336 out.write( encode3to4( b4, buffer, bufferLength ) );
1338 lineLength += 4;
1339 if( breakLines && lineLength >= MAX_LINE_LENGTH )
1341 out.write( NEW_LINE );
1342 lineLength = 0;
1343 } // end if: end of line
1345 position = 0;
1346 } // end if: enough to output
1347 } // end if: encoding
1349 // Else, Decoding
1350 else
1352 // Meaningful Base64 character?
1353 if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1355 buffer[ position++ ] = (byte)theByte;
1356 if( position >= bufferLength ) // Enough to output.
1358 int len = Base64.decode4to3( buffer, 0, b4, 0 );
1359 out.write( b4, 0, len );
1360 //out.write( Base64.decode4to3( buffer ) );
1361 position = 0;
1362 } // end if: enough to output
1363 } // end if: meaningful base64 character
1364 else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1366 throw new java.io.IOException( "Invalid character in Base64 data." );
1367 } // end else: not white space either
1368 } // end else: decoding
1369 } // end write
1374 * Calls {@link #write(int)} repeatedly until <var>len</var>
1375 * bytes are written.
1377 * @param theBytes array from which to read bytes
1378 * @param off offset for array
1379 * @param len max number of bytes to read into array
1380 * @since 1.3
1382 public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
1384 // Encoding suspended?
1385 if( suspendEncoding )
1387 super.out.write( theBytes, off, len );
1388 return;
1389 } // end if: suspended
1391 for( int i = 0; i < len; i++ )
1393 write( theBytes[ off + i ] );
1394 } // end for: each byte written
1396 } // end write
1401 * Method added by PHIL. [Thanks, PHIL. -Rob]
1402 * This pads the buffer without closing the stream.
1403 * @throws java.io.IOException input was not properly padded.
1405 public void flushBase64() throws java.io.IOException
1407 if( position > 0 )
1409 if( encode )
1411 out.write( encode3to4( b4, buffer, position ) );
1412 position = 0;
1413 } // end if: encoding
1414 else
1416 throw new java.io.IOException( "Base64 input not properly padded." );
1417 } // end else: decoding
1418 } // end if: buffer partially full
1420 } // end flush
1424 * Flushes and closes (I think, in the superclass) the stream.
1426 * @since 1.3
1428 public void close() throws java.io.IOException
1430 // 1. Ensure that pending characters are written
1431 flushBase64();
1433 // 2. Actually close the stream
1434 // Base class both flushes and closes.
1435 super.close();
1437 buffer = null;
1438 out = null;
1439 } // end close
1444 * Suspends encoding of the stream.
1445 * May be helpful if you need to embed a piece of
1446 * base640-encoded data in a stream.
1448 * @throws java.io.IOException input was not properly padded.
1449 * @since 1.5.1
1451 public void suspendEncoding() throws java.io.IOException
1453 flushBase64();
1454 this.suspendEncoding = true;
1455 } // end suspendEncoding
1459 * Resumes encoding of the stream.
1460 * May be helpful if you need to embed a piece of
1461 * base640-encoded data in a stream.
1463 * @since 1.5.1
1465 public void resumeEncoding()
1467 this.suspendEncoding = false;
1468 } // end resumeEncoding
1472 } // end inner class OutputStream
1475 } // end class Base64