Correctly use a long for the offsets within a generated pack
[egit/charleso.git] / org.spearce.jgit / src / org / spearce / jgit / util / Base64.java
blob8c4553996e037cd5b5f79d92f8138d39d40e2217
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;
11 /**
12 * Encodes and decodes to and from Base64 notation.
14 * <p>
15 * Change Log:
16 * </p>
17 * <ul>
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>
44 * </ul>
46 * <p>
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.
52 * </p>
54 * @author Robert Harder
55 * @author rob@iharder.net
56 * @version 2.1
58 public class Base64
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. */
119 static
121 byte[] __bytes;
124 __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING );
125 } // end try
126 catch (java.io.UnsupportedEncodingException use)
128 __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
129 } // end catch
130 ALPHABET = __bytes;
131 } // end static
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. */
180 private Base64(){}
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.
200 * @since 1.5.1
202 private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes )
204 encode3to4( threeBytes, 0, numSigBytes, b4, 0 );
205 return b4;
206 } // end encode3to4
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
228 * @since 1.3
230 private static byte[] encode3to4(
231 byte[] source, int srcOffset, int numSigBytes,
232 byte[] destination, int destOffset )
234 // 1 2 3
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 )
251 case 3:
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 ];
256 return destination;
258 case 2:
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;
263 return destination;
265 case 1:
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;
270 return destination;
272 default:
273 return destination;
274 } // end switch
275 } // end encode3to4
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
288 * @since 1.4
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>.
302 * <p>
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>
307 * </pre>
308 * <p>
309 * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
310 * <p>
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
316 * @see Base64#GZIP
317 * @see Base64#DONT_BREAK_LINES
318 * @since 2.0
320 public static String encodeObject( java.io.Serializable serializableObject, int options )
322 // Streams
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;
328 // Isolate options
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 );
338 // GZip?
339 if( gzip == GZIP )
341 gzos = new java.util.zip.GZIPOutputStream( b64os );
342 oos = new java.io.ObjectOutputStream( gzos );
343 } // end if: gzip
344 else
345 oos = new java.io.ObjectOutputStream( b64os );
347 oos.writeObject( serializableObject );
348 } // end try
349 catch( java.io.IOException e )
351 e.printStackTrace();
352 return null;
353 } // end catch
354 finally
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 ){}
360 } // end finally
362 // Return value according to relevant encoding.
365 return new String( baos.toByteArray(), PREFERRED_ENCODING );
366 } // end try
367 catch (java.io.UnsupportedEncodingException uue)
369 return new String( baos.toByteArray() );
370 } // end catch
372 } // end encode
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.
382 * @since 1.4
384 public static String encodeBytes( byte[] source )
386 return encodeBytes( source, 0, source.length, NO_OPTIONS );
387 } // end encodeBytes
392 * Encodes a byte array into Base64 notation.
393 * <p>
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>
398 * </pre>
399 * <p>
400 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
401 * <p>
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.
408 * @see Base64#GZIP
409 * @see Base64#DONT_BREAK_LINES
410 * @since 2.0
412 public static String encodeBytes( byte[] source, int options )
414 return encodeBytes( source, 0, source.length, options );
415 } // end encodeBytes
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.
426 * @since 1.4
428 public static String encodeBytes( byte[] source, int off, int len )
430 return encodeBytes( source, off, len, NO_OPTIONS );
431 } // end encodeBytes
436 * Encodes a byte array into Base64 notation.
437 * <p>
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>
442 * </pre>
443 * <p>
444 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
445 * <p>
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.
454 * @see Base64#GZIP
455 * @see Base64#DONT_BREAK_LINES
456 * @since 2.0
458 public static String encodeBytes( byte[] source, int off, int len, int options )
460 // Isolate options
461 int dontBreakLines = ( options & DONT_BREAK_LINES );
462 int gzip = ( options & GZIP );
464 // Compress?
465 if( gzip == 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 );
480 gzos.close();
481 } // end try
482 catch( java.io.IOException e )
484 e.printStackTrace();
485 return null;
486 } // end catch
487 finally
489 try{ gzos.close(); } catch( Exception e ){}
490 try{ b64os.close(); } catch( Exception e ){}
491 try{ baos.close(); } catch( Exception e ){}
492 } // end finally
494 // Return value according to relevant encoding.
497 return new String( baos.toByteArray(), PREFERRED_ENCODING );
498 } // end try
499 catch (java.io.UnsupportedEncodingException uue)
501 return new String( baos.toByteArray() );
502 } // end catch
503 } // end if: compress
505 // Else, don't compress. Better not to use streams at all then.
506 else
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
515 int d = 0;
516 int e = 0;
517 int len2 = len - 2;
518 int lineLength = 0;
519 for( ; d < len2; d+=3, e+=4 )
521 encode3to4( source, d+off, 3, outBuff, e );
523 lineLength += 4;
524 if( breakLines && lineLength == MAX_LINE_LENGTH )
526 outBuff[e+4] = NEW_LINE;
527 e++;
528 lineLength = 0;
529 } // end if: end of line
530 } // end for: each piece of array
532 if( d < len )
534 encode3to4( source, d+off, len - d, outBuff, e );
535 e += 4;
536 } // end if: some padding needed
539 // Return value according to relevant encoding.
542 return new String( outBuff, 0, e, PREFERRED_ENCODING );
543 } // end try
544 catch (java.io.UnsupportedEncodingException uue)
546 return new String( outBuff, 0, e );
547 } // end catch
549 } // end else: don't compress
551 } // end encodeBytes
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
580 * @since 1.3
582 private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
584 // Example: Dk==
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 );
594 return 1;
597 // Example: DkL=
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 );
610 return 2;
613 // Example: DkLE
614 else
616 try{
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 );
632 return 3;
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 ] ] ) );
638 return -1;
639 } //e nd catch
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
655 * @since 1.3
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
661 int outBuffPosn = 0;
663 byte[] b4 = new byte[4];
664 int b4Posn = 0;
665 int i = 0;
666 byte sbiCrop = 0;
667 byte sbiDecode = 0;
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;
678 if( b4Posn > 3 )
680 outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
681 b4Posn = 0;
683 // If that was the equals sign, break out of 'for' loop
684 if( sbiCrop == EQUALS_SIGN )
685 break;
686 } // end if: quartet built
688 } // end if: equals sign or better
690 } // end if: white space, equals sign or better
691 else
693 System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
694 return null;
695 } // end else:
696 } // each input character
698 byte[] out = new byte[ outBuffPosn ];
699 System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
700 return out;
701 } // end decode
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
712 * @since 1.4
714 public static byte[] decode( String s )
716 byte[] bytes;
719 bytes = s.getBytes( PREFERRED_ENCODING );
720 } // end try
721 catch( java.io.UnsupportedEncodingException uee )
723 bytes = s.getBytes();
724 } // end catch
725 //</change>
727 // Decode
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];
743 int length = 0;
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();
759 } // end try
760 catch( java.io.IOException e )
762 // Just return originally-decoded bytes
763 } // end catch
764 finally
766 try{ baos.close(); } catch( Exception e ){}
767 try{ gzis.close(); } catch( Exception e ){}
768 try{ bais.close(); } catch( Exception e ){}
769 } // end finally
771 } // end if: gzipped
772 } // end if: bytes.length >= 2
774 return bytes;
775 } // end decode
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
786 * @since 1.5
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;
795 Object obj = null;
799 bais = new java.io.ByteArrayInputStream( objBytes );
800 ois = new java.io.ObjectInputStream( bais );
802 obj = ois.readObject();
803 } // end try
804 catch( java.io.IOException e )
806 e.printStackTrace();
807 obj = null;
808 } // end catch
809 catch( java.lang.ClassNotFoundException e )
811 e.printStackTrace();
812 obj = null;
813 } // end catch
814 finally
816 try{ bais.close(); } catch( Exception e ){}
817 try{ ois.close(); } catch( Exception e ){}
818 } // end finally
820 return obj;
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
832 * @since 2.1
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 );
843 success = true;
844 } // end try
845 catch( java.io.IOException e )
848 success = false;
849 } // end catch: IOException
850 finally
852 try{ bos.close(); } catch( Exception e ){}
853 } // end finally
855 return success;
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
866 * @since 2.1
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 ) );
877 success = true;
878 } // end try
879 catch( java.io.IOException e )
881 success = false;
882 } // end catch: IOException
883 finally
885 try{ bos.close(); } catch( Exception e ){}
886 } // end finally
888 return success;
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
901 * @since 2.1
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;
912 int length = 0;
913 int numBytes = 0;
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)." );
919 return null;
920 } // end if: file too big for int index
921 buffer = new byte[ (int)file.length() ];
923 // Open a stream
924 bis = new Base64.InputStream(
925 new java.io.BufferedInputStream(
926 new java.io.FileInputStream( file ) ), Base64.DECODE );
928 // Read until done
929 while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
930 length += numBytes;
932 // Save in a variable to return
933 decodedData = new byte[ length ];
934 System.arraycopy( buffer, 0, decodedData, 0, length );
936 } // end try
937 catch( java.io.IOException e )
939 System.err.println( "Error decoding from file " + filename );
940 } // end catch: IOException
941 finally
943 try{ bis.close(); } catch( Exception e) {}
944 } // end finally
946 return decodedData;
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
958 * @since 2.1
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) ];
969 int length = 0;
970 int numBytes = 0;
972 // Open a stream
973 bis = new Base64.InputStream(
974 new java.io.BufferedInputStream(
975 new java.io.FileInputStream( file ) ), Base64.ENCODE );
977 // Read until done
978 while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 )
979 length += numBytes;
981 // Save in a variable to return
982 encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
984 } // end try
985 catch( java.io.IOException e )
987 System.err.println( "Error encoding from file " + filename );
988 } // end catch: IOException
989 finally
991 try{ bis.close(); } catch( Exception e) {}
992 } // end finally
994 return encodedData;
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.
1009 * @see Base64
1010 * @since 1.3
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.
1027 * @since 1.3
1029 public InputStream( java.io.InputStream in )
1031 this( in, DECODE );
1032 } // end constructor
1036 * Constructs a {@link Base64.InputStream} in
1037 * either ENCODE or DECODE mode.
1038 * <p>
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>
1044 * </pre>
1045 * <p>
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
1054 * @since 2.0
1056 public InputStream( java.io.InputStream in, int options )
1058 super( in );
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 ];
1063 this.position = -1;
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.
1071 * @return next byte
1072 * @since 1.3
1074 public int read() throws java.io.IOException
1076 // Do we need to get data?
1077 if( position < 0 )
1079 if( encode )
1081 byte[] b3 = new byte[3];
1082 int numBinaryBytes = 0;
1083 for( int i = 0; i < 3; i++ )
1087 int b = in.read();
1089 // If end of stream, b is -1.
1090 if( b >= 0 )
1092 b3[i] = (byte)b;
1093 numBinaryBytes++;
1094 } // end if: not end of stream
1096 } // end try: read
1097 catch( java.io.IOException e )
1099 // Only a problem if we got no data at all.
1100 if( i == 0 )
1101 throw e;
1103 } // end catch
1104 } // end for: each needed input byte
1106 if( numBinaryBytes > 0 )
1108 encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
1109 position = 0;
1110 numSigBytes = 4;
1111 } // end if: got data
1112 else
1114 return -1;
1115 } // end else
1116 } // end if: encoding
1118 // Else decoding
1119 else
1121 byte[] b4 = new byte[4];
1122 int i = 0;
1123 for( i = 0; i < 4; i++ )
1125 // Read four "meaningful" bytes:
1126 int b = 0;
1127 do{ b = in.read(); }
1128 while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
1130 if( b < 0 )
1131 break; // Reads a -1 if end of stream
1133 b4[i] = (byte)b;
1134 } // end for: each needed input byte
1136 if( i == 4 )
1138 numSigBytes = decode4to3( b4, 0, buffer, 0 );
1139 position = 0;
1140 } // end if: got four characters
1141 else if( i == 0 ){
1142 return -1;
1143 } // end else if: also padded correctly
1144 else
1146 // Must have broken out from above.
1147 throw new java.io.IOException( "Improperly padded Base64 input." );
1148 } // end
1150 } // end else: decode
1151 } // end else: get data
1153 // Got data?
1154 if( position >= 0 )
1156 // End of relevant data?
1157 if( /*!encode &&*/ position >= numSigBytes )
1158 return -1;
1160 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
1162 lineLength = 0;
1163 return '\n';
1164 } // end if
1165 else
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 )
1174 position = -1;
1176 return b & 0xFF; // This is how you "cast" a byte that's
1177 // intended to be unsigned.
1178 } // end else
1179 } // end if: position >= 0
1181 // Else error
1182 else
1184 // When JDK1.4 is more accepted, use an assertion here.
1185 throw new java.io.IOException( "Error in Base64 code reading stream." );
1186 } // end else
1187 } // end read
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.
1200 * @since 1.3
1202 public int read( byte[] dest, int off, int len ) throws java.io.IOException
1204 int i;
1205 int b;
1206 for( i = 0; i < len; i++ )
1208 b = read();
1210 //if( b < 0 && i == 0 )
1211 // return -1;
1213 if( b >= 0 )
1214 dest[off + i] = (byte)b;
1215 else if( i == 0 )
1216 return -1;
1217 else
1218 break; // Out of 'for' loop
1219 } // end for: each byte read
1220 return i;
1221 } // end 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.
1239 * @see Base64
1240 * @since 1.3
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.
1257 * @since 1.3
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.
1268 * <p>
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>
1274 * </pre>
1275 * <p>
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
1283 * @since 1.3
1285 public OutputStream( java.io.OutputStream out, int options )
1287 super( out );
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 ];
1292 this.position = 0;
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
1306 * at a time.
1308 * @param theByte the byte to write
1309 * @since 1.3
1311 public void write(int theByte) throws java.io.IOException
1313 // Encoding suspended?
1314 if( suspendEncoding )
1316 super.out.write( theByte );
1317 return;
1318 } // end if: suspended
1320 // Encode?
1321 if( encode )
1323 buffer[ position++ ] = (byte)theByte;
1324 if( position >= bufferLength ) // Enough to encode.
1326 out.write( encode3to4( b4, buffer, bufferLength ) );
1328 lineLength += 4;
1329 if( breakLines && lineLength >= MAX_LINE_LENGTH )
1331 out.write( NEW_LINE );
1332 lineLength = 0;
1333 } // end if: end of line
1335 position = 0;
1336 } // end if: enough to output
1337 } // end if: encoding
1339 // Else, Decoding
1340 else
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 ) );
1351 position = 0;
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
1359 } // end write
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
1370 * @since 1.3
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 );
1378 return;
1379 } // end if: suspended
1381 for( int i = 0; i < len; i++ )
1383 write( theBytes[ off + i ] );
1384 } // end for: each byte written
1386 } // end write
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
1397 if( position > 0 )
1399 if( encode )
1401 out.write( encode3to4( b4, buffer, position ) );
1402 position = 0;
1403 } // end if: encoding
1404 else
1406 throw new java.io.IOException( "Base64 input not properly padded." );
1407 } // end else: decoding
1408 } // end if: buffer partially full
1410 } // end flush
1414 * Flushes and closes (I think, in the superclass) the stream.
1416 * @since 1.3
1418 public void close() throws java.io.IOException
1420 // 1. Ensure that pending characters are written
1421 flushBase64();
1423 // 2. Actually close the stream
1424 // Base class both flushes and closes.
1425 super.close();
1427 buffer = null;
1428 out = null;
1429 } // end close
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.
1439 * @since 1.5.1
1441 public void suspendEncoding() throws java.io.IOException
1443 flushBase64();
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.
1453 * @since 1.5.1
1455 public void resumeEncoding()
1457 this.suspendEncoding = false;
1458 } // end resumeEncoding
1462 } // end inner class OutputStream
1465 } // end class Base64