View Javadoc

1   package org.indi.server;
2   
3   /**
4    * <p>
5    * Encodes and decodes to and from Base64 notation.
6    * </p>
7    * <p>
8    * Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.
9    * </p>
10   * 
11   * <p>
12   * Change Log:
13   * </p>
14   * <ul>
15   * <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug when
16   * using very small files (~< 40 bytes).</li>
17   * <li>v2.2 - Added some helper methods for encoding/decoding directly from one
18   * file to the next. Also added a main() method to support command line
19   * encoding/decoding from one file to the next. Also added these Base64
20   * dialects:
21   * <ol>
22   * <li>The default is RFC3548 format.</li>
23   * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates
24   * URL and file name friendly format as described in Section 4 of RFC3548.
25   * http://www.faqs.org/rfcs/rfc3548.html</li>
26   * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates
27   * URL and file name friendly format that preserves lexical ordering as
28   * described in http://www.faqs.org/qa/rfcc-1940.html</li>
29   * </ol>
30   * Special thanks to Jim Kellerman at <a
31   * href="http://www.powerset.com/">http://www.powerset.com/</a> for
32   * contributing the new Base64 dialects. </li>
33   * 
34   * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods.
35   * Added some convenience methods for reading and writing to and from files.</li>
36   * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on
37   * systems with other encodings (like EBCDIC).</li>
38   * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
39   * encoded data was a single byte.</li>
40   * <li>v2.0 - I got rid of methods that used booleans to set options. Now
41   * everything is more consolidated and cleaner. The code now detects when data
42   * that's being decoded is gzip-compressed and will decompress it automatically.
43   * Generally things are cleaner. You'll probably have to change some method
44   * calls that you were making to support the new options format (<tt>int</tt>s
45   * that you "OR" together).</li>
46   * <li>v1.5.1 - Fixed bug when decompressing and decoding to a byte[] using
47   * <tt>decode( String s, boolean gzipCompressed )</tt>. Added the ability to
48   * "suspend" encoding in the Output Stream so you can turn on and off the
49   * encoding if you need to embed base64 data in an otherwise "normal" stream
50   * (like an XML file).</li>
51   * <li>v1.5 - Output stream pases on flush() command but doesn't do anything
52   * itself. This helps when using GZIP streams. Added the ability to
53   * GZip-compress objects before encoding them.</li>
54   * <li>v1.4 - Added helper methods to read/write files.</li>
55   * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
56   * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input
57   * stream where last buffer being read, if not completely full, was not
58   * returned.</li>
59   * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the
60   * wrong time.</li>
61   * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
62   * </ul>
63   * 
64   * <p>
65   * I am placing this code in the Public Domain. Do with it as you will. This
66   * software comes with no guarantees or warranties but with plenty of
67   * well-wishing instead! Please visit <a
68   * href="http://iharder.net/base64">http://iharder.net/base64</a> periodically
69   * to check for updates or to contribute improvements.
70   * </p>
71   * 
72   * @author Robert Harder
73   * @author rob@iharder.net
74   * @version 2.2.1
75   */
76  public class Base64 {
77  
78      /* ******** P U B L I C F I E L D S ******** */
79  
80      /** No options specified. Value is zero. */
81      public final static int NO_OPTIONS = 0;
82  
83      /** Specify encoding. */
84      public final static int ENCODE = 1;
85  
86      /** Specify decoding. */
87      public final static int DECODE = 0;
88  
89      /** Specify that data should be gzip-compressed. */
90      public final static int GZIP = 2;
91  
92      /** Don't break lines when encoding (violates strict Base64 specification) */
93      public final static int DONT_BREAK_LINES = 8;
94  
95      /**
96       * Encode using Base64-like encoding that is URL- and Filename-safe as
97       * described in Section 4 of RFC3548: <a
98       * href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
99       * It is important to note that data encoded this way is <em>not</em>
100      * officially valid Base64, or at the very least should not be called Base64
101      * without also specifying that is was encoded using the URL- and
102      * Filename-safe dialect.
103      */
104     public final static int URL_SAFE = 16;
105 
106     /**
107      * Encode using the special "ordered" dialect of Base64 described here: <a
108      * href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
109      */
110     public final static int ORDERED = 32;
111 
112     /* ******** P R I V A T E F I E L D S ******** */
113 
114     /** Maximum line length (76) of Base64 output. */
115     private final static int MAX_LINE_LENGTH = 76;
116 
117     /** The equals sign (=) as a byte. */
118     private final static byte EQUALS_SIGN = (byte) '=';
119 
120     /** The new line character (\n) as a byte. */
121     private final static byte NEW_LINE = (byte) '\n';
122 
123     /** Preferred encoding. */
124     private final static String PREFERRED_ENCODING = "UTF-8";
125 
126     // I think I end up not using the BAD_ENCODING indicator.
127     // private final static byte BAD_ENCODING = -9; // Indicates error in
128     // encoding
129     private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in
130 						    // encoding
131     private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in
132 						    // encoding
133 
134     /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
135 
136     /** The 64 valid Base64 values. */
137     // private final static byte[] ALPHABET;
138     /*
139      * Host platform me be something funny like EBCDIC, so we hardcode these
140      * values.
141      */
142     private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B',
143 	    (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
144 	    (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
145 	    (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q',
146 	    (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
147 	    (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
148 	    (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
149 	    (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
150 	    (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
151 	    (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
152 	    (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
153 	    (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
154 	    (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
155 	    (byte) '+', (byte) '/' };
156 
157     /**
158      * Translates a Base64 value to either its 6-bit reconstruction value or a
159      * negative number indicating some other meaning.
160      */
161     private final static byte[] _STANDARD_DECODABET = { -9, -9, -9, -9, -9, -9,
162 	    -9, -9, -9, // Decimal 0 - 8
163 	    -5, -5, // Whitespace: Tab and Linefeed
164 	    -9, -9, // Decimal 11 - 12
165 	    -5, // Whitespace: Carriage Return
166 	    -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
167 								// 26
168 	    -9, -9, -9, -9, -9, // Decimal 27 - 31
169 	    -5, // Whitespace: Space
170 	    -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
171 	    62, // Plus sign at decimal 43
172 	    -9, -9, -9, // Decimal 44 - 46
173 	    63, // Slash at decimal 47
174 	    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
175 	    -9, -9, -9, // Decimal 58 - 60
176 	    -1, // Equals sign at decimal 61
177 	    -9, -9, -9, // Decimal 62 - 64
178 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A'
179 							    // through 'N'
180 	    14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
181 							    // through 'Z'
182 	    -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
183 	    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
184 								// through 'm'
185 	    39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
186 								// through 'z'
187 	    -9, -9, -9, -9 // Decimal 123 - 126
188     /*
189      * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
190      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
191      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
192      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
193      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
194      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
195      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
196      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
197      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
198      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255
199      */
200     };
201 
202     /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
203 
204     /**
205      * Used in the URL- and Filename-safe dialect described in Section 4 of
206      * RFC3548: <a
207      * href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
208      * Notice that the last two bytes become "hyphen" and "underscore" instead
209      * of "plus" and "slash."
210      */
211     private final static byte[] _URL_SAFE_ALPHABET = { (byte) 'A', (byte) 'B',
212 	    (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G',
213 	    (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
214 	    (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q',
215 	    (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V',
216 	    (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a',
217 	    (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
218 	    (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
219 	    (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
220 	    (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
221 	    (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z',
222 	    (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4',
223 	    (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
224 	    (byte) '-', (byte) '_' };
225 
226     /**
227      * Used in decoding URL- and Filename-safe dialects of Base64.
228      */
229     private final static byte[] _URL_SAFE_DECODABET = { -9, -9, -9, -9, -9, -9,
230 	    -9, -9, -9, // Decimal 0 - 8
231 	    -5, -5, // Whitespace: Tab and Linefeed
232 	    -9, -9, // Decimal 11 - 12
233 	    -5, // Whitespace: Carriage Return
234 	    -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
235 								// 26
236 	    -9, -9, -9, -9, -9, // Decimal 27 - 31
237 	    -5, // Whitespace: Space
238 	    -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
239 	    -9, // Plus sign at decimal 43
240 	    -9, // Decimal 44
241 	    62, // Minus sign at decimal 45
242 	    -9, // Decimal 46
243 	    -9, // Slash at decimal 47
244 	    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
245 	    -9, -9, -9, // Decimal 58 - 60
246 	    -1, // Equals sign at decimal 61
247 	    -9, -9, -9, // Decimal 62 - 64
248 	    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A'
249 							    // through 'N'
250 	    14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
251 							    // through 'Z'
252 	    -9, -9, -9, -9, // Decimal 91 - 94
253 	    63, // Underscore at decimal 95
254 	    -9, // Decimal 96
255 	    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
256 								// through 'm'
257 	    39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
258 								// through 'z'
259 	    -9, -9, -9, -9 // Decimal 123 - 126
260     /*
261      * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
262      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
263      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
264      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
265      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
266      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
267      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
268      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
269      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
270      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255
271      */
272     };
273 
274     /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
275 
276     /**
277      * I don't get the point of this technique, but it is described here: <a
278      * href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
279      */
280     private final static byte[] _ORDERED_ALPHABET = { (byte) '-', (byte) '0',
281 	    (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
282 	    (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A',
283 	    (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
284 	    (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
285 	    (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
286 	    (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
287 	    (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
288 	    (byte) '_', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
289 	    (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i',
290 	    (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
291 	    (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's',
292 	    (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
293 	    (byte) 'y', (byte) 'z' };
294 
295     /**
296      * Used in decoding the "ordered" dialect of Base64.
297      */
298     private final static byte[] _ORDERED_DECODABET = { -9, -9, -9, -9, -9, -9,
299 	    -9, -9, -9, // Decimal 0 - 8
300 	    -5, -5, // Whitespace: Tab and Linefeed
301 	    -9, -9, // Decimal 11 - 12
302 	    -5, // Whitespace: Carriage Return
303 	    -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
304 								// 26
305 	    -9, -9, -9, -9, -9, // Decimal 27 - 31
306 	    -5, // Whitespace: Space
307 	    -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
308 	    -9, // Plus sign at decimal 43
309 	    -9, // Decimal 44
310 	    0, // Minus sign at decimal 45
311 	    -9, // Decimal 46
312 	    -9, // Slash at decimal 47
313 	    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine
314 	    -9, -9, -9, // Decimal 58 - 60
315 	    -1, // Equals sign at decimal 61
316 	    -9, -9, -9, // Decimal 62 - 64
317 	    11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A'
318 								// through 'M'
319 	    24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N'
320 								// through 'Z'
321 	    -9, -9, -9, -9, // Decimal 91 - 94
322 	    37, // Underscore at decimal 95
323 	    -9, // Decimal 96
324 	    38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a'
325 								// through 'm'
326 	    51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n'
327 								// through 'z'
328 	    -9, -9, -9, -9 // Decimal 123 - 126
329     /*
330      * ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
331      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
332      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
333      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
334      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
335      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
336      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
337      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
338      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
339      * -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255
340      */
341     };
342 
343     /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */
344 
345     /**
346      * Returns one of the _SOMETHING_ALPHABET byte arrays depending on the
347      * options specified. It's possible, though silly, to specify ORDERED and
348      * URLSAFE in which case one of them will be picked, though there is no
349      * guarantee as to which one will be picked.
350      */
351     private final static byte[] getAlphabet(int options) {
352 	if ((options & Base64.URL_SAFE) == Base64.URL_SAFE) {
353 	    return Base64._URL_SAFE_ALPHABET;
354 	} else if ((options & Base64.ORDERED) == Base64.ORDERED) {
355 	    return Base64._ORDERED_ALPHABET;
356 	} else {
357 	    return Base64._STANDARD_ALPHABET;
358 	}
359 
360     } // end getAlphabet
361 
362     /**
363      * Returns one of the _SOMETHING_DECODABET byte arrays depending on the
364      * options specified. It's possible, though silly, to specify ORDERED and
365      * URL_SAFE in which case one of them will be picked, though there is no
366      * guarantee as to which one will be picked.
367      */
368     private final static byte[] getDecodabet(int options) {
369 	if ((options & Base64.URL_SAFE) == Base64.URL_SAFE) {
370 	    return Base64._URL_SAFE_DECODABET;
371 	} else if ((options & Base64.ORDERED) == Base64.ORDERED) {
372 	    return Base64._ORDERED_DECODABET;
373 	} else {
374 	    return Base64._STANDARD_DECODABET;
375 	}
376 
377     } // end getAlphabet
378 
379     /** Defeats instantiation. */
380     private Base64() {
381     }
382 
383     /**
384      * Encodes or decodes two files from the command line; <strong>feel free to
385      * delete this method (in fact you probably should) if you're embedding this
386      * code into a larger program.</strong>
387      */
388     public final static void main(String[] args) {
389 	if (args.length < 3) {
390 	    usage("Not enough arguments.");
391 	} // end if: args.length < 3
392 	else {
393 	    String flag = args[0];
394 	    String infile = args[1];
395 	    String outfile = args[2];
396 	    if (flag.equals("-e")) {
397 		Base64.encodeFileToFile(infile, outfile);
398 	    } // end if: encode
399 	    else if (flag.equals("-d")) {
400 		Base64.decodeFileToFile(infile, outfile);
401 	    } // end else if: decode
402 	    else {
403 		usage("Unknown flag: " + flag);
404 	    } // end else
405 	} // end else
406     } // end main
407 
408     /**
409      * Prints command line usage.
410      * 
411      * @param msg
412      *                A message to include with usage info.
413      */
414     private final static void usage(String msg) {
415 	System.err.println(msg);
416 	System.err.println("Usage: java Base64 -e|-d inputfile outputfile");
417     } // end usage
418 
419     /* ******** E N C O D I N G M E T H O D S ******** */
420 
421     /**
422      * Encodes up to the first three bytes of array <var>threeBytes</var> and
423      * returns a four-byte array in Base64 notation. The actual number of
424      * significant bytes in your array is given by <var>numSigBytes</var>. The
425      * array <var>threeBytes</var> needs only be as big as <var>numSigBytes</var>.
426      * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
427      * 
428      * @param b4
429      *                A reusable byte array to reduce array instantiation
430      * @param threeBytes
431      *                the array to convert
432      * @param numSigBytes
433      *                the number of significant bytes in your array
434      * @return four byte array in Base64 notation.
435      * @since 1.5.1
436      */
437     private static byte[] encode3to4(byte[] b4, byte[] threeBytes,
438 	    int numSigBytes, int options) {
439 	encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
440 	return b4;
441     } // end encode3to4
442 
443     /**
444      * <p>
445      * Encodes up to three bytes of the array <var>source</var> and writes the
446      * resulting four Base64 bytes to <var>destination</var>. The source and
447      * destination arrays can be manipulated anywhere along their length by
448      * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
449      * does not check to make sure your arrays are large enough to accomodate
450      * <var>srcOffset</var> + 3 for the <var>source</var> array or
451      * <var>destOffset</var> + 4 for the <var>destination</var> array. The
452      * actual number of significant bytes in your array is given by
453      * <var>numSigBytes</var>.
454      * </p>
455      * <p>
456      * This is the lowest level of the encoding methods with all possible
457      * parameters.
458      * </p>
459      * 
460      * @param source
461      *                the array to convert
462      * @param srcOffset
463      *                the index where conversion begins
464      * @param numSigBytes
465      *                the number of significant bytes in your array
466      * @param destination
467      *                the array to hold the conversion
468      * @param destOffset
469      *                the index where output will be put
470      * @return the <var>destination</var> array
471      * @since 1.3
472      */
473     private static byte[] encode3to4(byte[] source, int srcOffset,
474 	    int numSigBytes, byte[] destination, int destOffset, int options) {
475 	byte[] ALPHABET = getAlphabet(options);
476 
477 	// 1 2 3
478 	// 01234567890123456789012345678901 Bit position
479 	// --------000000001111111122222222 Array position from threeBytes
480 	// --------| || || || | Six bit groups to index ALPHABET
481 	// >>18 >>12 >> 6 >> 0 Right shift necessary
482 	// 0x3f 0x3f 0x3f Additional AND
483 
484 	// Create buffer with zero-padding if there are only one or two
485 	// significant bytes passed in the array.
486 	// We have to shift left 24 in order to flush out the 1's that appear
487 	// when Java treats a value as negative that is cast from a byte to an
488 	// int.
489 	int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
490 		| (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
491 		| (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
492 
493 	switch (numSigBytes) {
494 	case 3:
495 	    destination[destOffset] = ALPHABET[(inBuff >>> 18)];
496 	    destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
497 	    destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
498 	    destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
499 	    return destination;
500 
501 	case 2:
502 	    destination[destOffset] = ALPHABET[(inBuff >>> 18)];
503 	    destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
504 	    destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
505 	    destination[destOffset + 3] = Base64.EQUALS_SIGN;
506 	    return destination;
507 
508 	case 1:
509 	    destination[destOffset] = ALPHABET[(inBuff >>> 18)];
510 	    destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
511 	    destination[destOffset + 2] = Base64.EQUALS_SIGN;
512 	    destination[destOffset + 3] = Base64.EQUALS_SIGN;
513 	    return destination;
514 
515 	default:
516 	    return destination;
517 	} // end switch
518     } // end encode3to4
519 
520     /**
521      * Serializes an object and returns the Base64-encoded version of that
522      * serialized object. If the object cannot be serialized or there is another
523      * error, the method will return <tt>null</tt>. The object is not
524      * GZip-compressed before being encoded.
525      * 
526      * @param serializableObject
527      *                The object to encode
528      * @return The Base64-encoded object
529      * @since 1.4
530      */
531     public static String encodeObject(java.io.Serializable serializableObject) {
532 	return encodeObject(serializableObject, Base64.NO_OPTIONS);
533     } // end encodeObject
534 
535     /**
536      * Serializes an object and returns the Base64-encoded version of that
537      * serialized object. If the object cannot be serialized or there is another
538      * error, the method will return <tt>null</tt>.
539      * <p>
540      * Valid options:
541      * 
542      * <pre>
543      *   GZIP: gzip-compresses object before encoding it.
544      *   DONT_BREAK_LINES: don't break lines at 76 characters
545      *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
546      * </pre>
547      * 
548      * <p>
549      * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
550      * <p>
551      * Example:
552      * <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
553      * 
554      * @param serializableObject
555      *                The object to encode
556      * @param options
557      *                Specified options
558      * @return The Base64-encoded object
559      * @see Base64#GZIP
560      * @see Base64#DONT_BREAK_LINES
561      * @since 2.0
562      */
563     public static String encodeObject(java.io.Serializable serializableObject,
564 	    int options) {
565 	// Streams
566 	java.io.ByteArrayOutputStream baos = null;
567 	java.io.OutputStream b64os = null;
568 	java.io.ObjectOutputStream oos = null;
569 	java.util.zip.GZIPOutputStream gzos = null;
570 
571 	// Isolate options
572 	int gzip = (options & Base64.GZIP);
573 	@SuppressWarnings("unused")
574 	int dontBreakLines = (options & Base64.DONT_BREAK_LINES);
575 
576 	try {
577 	    // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
578 	    baos = new java.io.ByteArrayOutputStream();
579 	    b64os = new Base64.OutputStream(baos, Base64.ENCODE | options);
580 
581 	    // GZip?
582 	    if (gzip == Base64.GZIP) {
583 		gzos = new java.util.zip.GZIPOutputStream(b64os);
584 		oos = new java.io.ObjectOutputStream(gzos);
585 	    } // end if: gzip
586 	    else {
587 		oos = new java.io.ObjectOutputStream(b64os);
588 	    }
589 
590 	    oos.writeObject(serializableObject);
591 	} // end try
592 	catch (java.io.IOException e) {
593 	    e.printStackTrace();
594 	    return null;
595 	} // end catch
596 	finally {
597 	    try {
598 		oos.close();
599 	    } catch (Exception e) {
600 	    }
601 	    try {
602 		gzos.close();
603 	    } catch (Exception e) {
604 	    }
605 	    try {
606 		b64os.close();
607 	    } catch (Exception e) {
608 	    }
609 	    try {
610 		baos.close();
611 	    } catch (Exception e) {
612 	    }
613 	} // end finally
614 
615 	// Return value according to relevant encoding.
616 	try {
617 	    return new String(baos.toByteArray(), Base64.PREFERRED_ENCODING);
618 	} // end try
619 	catch (java.io.UnsupportedEncodingException uue) {
620 	    return new String(baos.toByteArray());
621 	} // end catch
622 
623     } // end encode
624 
625     /**
626      * Encodes a byte array into Base64 notation. Does not GZip-compress data.
627      * 
628      * @param source
629      *                The data to convert
630      * @since 1.4
631      */
632     public static String encodeBytes(byte[] source) {
633 	return encodeBytes(source, 0, source.length, Base64.NO_OPTIONS);
634     } // end encodeBytes
635 
636     /**
637      * Encodes a byte array into Base64 notation.
638      * <p>
639      * Valid options:
640      * 
641      * <pre>
642      *   GZIP: gzip-compresses object before encoding it.
643      *   DONT_BREAK_LINES: don't break lines at 76 characters
644      *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
645      * </pre>
646      * 
647      * <p>
648      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
649      * <p>
650      * Example:
651      * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
652      * 
653      * 
654      * @param source
655      *                The data to convert
656      * @param options
657      *                Specified options
658      * @see Base64#GZIP
659      * @see Base64#DONT_BREAK_LINES
660      * @since 2.0
661      */
662     public static String encodeBytes(byte[] source, int options) {
663 	return encodeBytes(source, 0, source.length, options);
664     } // end encodeBytes
665 
666     /**
667      * Encodes a byte array into Base64 notation. Does not GZip-compress data.
668      * 
669      * @param source
670      *                The data to convert
671      * @param off
672      *                Offset in array where conversion should begin
673      * @param len
674      *                Length of data to convert
675      * @since 1.4
676      */
677     public static String encodeBytes(byte[] source, int off, int len) {
678 	return encodeBytes(source, off, len, Base64.NO_OPTIONS);
679     } // end encodeBytes
680 
681     /**
682      * Encodes a byte array into Base64 notation.
683      * <p>
684      * Valid options:
685      * 
686      * <pre>
687      *   GZIP: gzip-compresses object before encoding it.
688      *   DONT_BREAK_LINES: don't break lines at 76 characters
689      *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
690      * </pre>
691      * 
692      * <p>
693      * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
694      * <p>
695      * Example:
696      * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
697      * 
698      * 
699      * @param source
700      *                The data to convert
701      * @param off
702      *                Offset in array where conversion should begin
703      * @param len
704      *                Length of data to convert
705      * @param options
706      *                Specified options
707      * @param options
708      *                alphabet type is pulled from this (standard, url-safe,
709      *                ordered)
710      * @see Base64#GZIP
711      * @see Base64#DONT_BREAK_LINES
712      * @since 2.0
713      */
714     public static String encodeBytes(byte[] source, int off, int len,
715 	    int options) {
716 	// Isolate options
717 	int dontBreakLines = (options & Base64.DONT_BREAK_LINES);
718 	int gzip = (options & Base64.GZIP);
719 
720 	// Compress?
721 	if (gzip == Base64.GZIP) {
722 	    java.io.ByteArrayOutputStream baos = null;
723 	    java.util.zip.GZIPOutputStream gzos = null;
724 	    Base64.OutputStream b64os = null;
725 
726 	    try {
727 		// GZip -> Base64 -> ByteArray
728 		baos = new java.io.ByteArrayOutputStream();
729 		b64os = new Base64.OutputStream(baos, Base64.ENCODE | options);
730 		gzos = new java.util.zip.GZIPOutputStream(b64os);
731 
732 		gzos.write(source, off, len);
733 		gzos.close();
734 	    } // end try
735 	    catch (java.io.IOException e) {
736 		e.printStackTrace();
737 		return null;
738 	    } // end catch
739 	    finally {
740 		try {
741 		    gzos.close();
742 		} catch (Exception e) {
743 		}
744 		try {
745 		    b64os.close();
746 		} catch (Exception e) {
747 		}
748 		try {
749 		    baos.close();
750 		} catch (Exception e) {
751 		}
752 	    } // end finally
753 
754 	    // Return value according to relevant encoding.
755 	    try {
756 		return new String(baos.toByteArray(), Base64.PREFERRED_ENCODING);
757 	    } // end try
758 	    catch (java.io.UnsupportedEncodingException uue) {
759 		return new String(baos.toByteArray());
760 	    } // end catch
761 	} // end if: compress
762 
763 	// Else, don't compress. Better not to use streams at all then.
764 	else {
765 	    // Convert option to boolean in way that code likes it.
766 	    boolean breakLines = dontBreakLines == 0;
767 
768 	    int len43 = len * 4 / 3;
769 	    byte[] outBuff = new byte[(len43) // Main 4:3
770 		    + ((len % 3) > 0 ? 4 : 0) // Account for padding
771 		    + (breakLines ? (len43 / Base64.MAX_LINE_LENGTH) : 0)]; // New
772 									    // lines
773 	    int d = 0;
774 	    int e = 0;
775 	    int len2 = len - 2;
776 	    int lineLength = 0;
777 	    for (; d < len2; d += 3, e += 4) {
778 		encode3to4(source, d + off, 3, outBuff, e, options);
779 
780 		lineLength += 4;
781 		if (breakLines && lineLength == Base64.MAX_LINE_LENGTH) {
782 		    outBuff[e + 4] = Base64.NEW_LINE;
783 		    e++;
784 		    lineLength = 0;
785 		} // end if: end of line
786 	    } // en dfor: each piece of array
787 
788 	    if (d < len) {
789 		encode3to4(source, d + off, len - d, outBuff, e, options);
790 		e += 4;
791 	    } // end if: some padding needed
792 
793 	    // Return value according to relevant encoding.
794 	    try {
795 		return new String(outBuff, 0, e, Base64.PREFERRED_ENCODING);
796 	    } // end try
797 	    catch (java.io.UnsupportedEncodingException uue) {
798 		return new String(outBuff, 0, e);
799 	    } // end catch
800 
801 	} // end else: don't compress
802 
803     } // end encodeBytes
804 
805     /* ******** D E C O D I N G M E T H O D S ******** */
806 
807     /**
808      * Decodes four bytes from array <var>source</var> and writes the resulting
809      * bytes (up to three of them) to <var>destination</var>. The source and
810      * destination arrays can be manipulated anywhere along their length by
811      * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
812      * does not check to make sure your arrays are large enough to accomodate
813      * <var>srcOffset</var> + 4 for the <var>source</var> array or
814      * <var>destOffset</var> + 3 for the <var>destination</var> array. This
815      * method returns the actual number of bytes that were converted from the
816      * Base64 encoding.
817      * <p>
818      * This is the lowest level of the decoding methods with all possible
819      * parameters.
820      * </p>
821      * 
822      * 
823      * @param source
824      *                the array to convert
825      * @param srcOffset
826      *                the index where conversion begins
827      * @param destination
828      *                the array to hold the conversion
829      * @param destOffset
830      *                the index where output will be put
831      * @param options
832      *                alphabet type is pulled from this (standard, url-safe,
833      *                ordered)
834      * @return the number of decoded bytes converted
835      * @since 1.3
836      */
837     private static int decode4to3(byte[] source, int srcOffset,
838 	    byte[] destination, int destOffset, int options) {
839 	byte[] DECODABET = getDecodabet(options);
840 
841 	// Example: Dk==
842 	if (source[srcOffset + 2] == Base64.EQUALS_SIGN) {
843 	    // Two ways to do the same thing. Don't know which way I like best.
844 	    // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
845 	    // )
846 	    // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
847 	    int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
848 		    | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
849 
850 	    destination[destOffset] = (byte) (outBuff >>> 16);
851 	    return 1;
852 	}
853 
854 	// Example: DkL=
855 	else if (source[srcOffset + 3] == Base64.EQUALS_SIGN) {
856 	    // Two ways to do the same thing. Don't know which way I like best.
857 	    // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
858 	    // )
859 	    // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
860 	    // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
861 	    int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
862 		    | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
863 		    | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
864 
865 	    destination[destOffset] = (byte) (outBuff >>> 16);
866 	    destination[destOffset + 1] = (byte) (outBuff >>> 8);
867 	    return 2;
868 	}
869 
870 	// Example: DkLE
871 	else {
872 	    try {
873 		// Two ways to do the same thing. Don't know which way I like
874 		// best.
875 		// int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 )
876 		// >>> 6 )
877 		// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
878 		// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
879 		// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
880 		int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
881 			| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
882 			| ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
883 			| ((DECODABET[source[srcOffset + 3]] & 0xFF));
884 
885 		destination[destOffset] = (byte) (outBuff >> 16);
886 		destination[destOffset + 1] = (byte) (outBuff >> 8);
887 		destination[destOffset + 2] = (byte) (outBuff);
888 
889 		return 3;
890 	    } catch (Exception e) {
891 		System.out.println("" + source[srcOffset] + ": "
892 			+ (DECODABET[source[srcOffset]]));
893 		System.out.println("" + source[srcOffset + 1] + ": "
894 			+ (DECODABET[source[srcOffset + 1]]));
895 		System.out.println("" + source[srcOffset + 2] + ": "
896 			+ (DECODABET[source[srcOffset + 2]]));
897 		System.out.println("" + source[srcOffset + 3] + ": "
898 			+ (DECODABET[source[srcOffset + 3]]));
899 		return -1;
900 	    } // end catch
901 	}
902     } // end decodeToBytes
903 
904     /**
905      * Very low-level access to decoding ASCII characters in the form of a byte
906      * array. Does not support automatically gunzipping or any other "fancy"
907      * features.
908      * 
909      * @param source
910      *                The Base64 encoded data
911      * @param off
912      *                The offset of where to begin decoding
913      * @param len
914      *                The length of characters to decode
915      * @return decoded data
916      * @since 1.3
917      */
918     public static byte[] decode(byte[] source, int off, int len, int options) {
919 	byte[] DECODABET = getDecodabet(options);
920 
921 	int len34 = len * 3 / 4;
922 	byte[] outBuff = new byte[len34]; // Upper limit on size of output
923 	int outBuffPosn = 0;
924 
925 	byte[] b4 = new byte[4];
926 	int b4Posn = 0;
927 	int i = 0;
928 	byte sbiCrop = 0;
929 	byte sbiDecode = 0;
930 	for (i = off; i < off + len; i++) {
931 	    sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
932 	    sbiDecode = DECODABET[sbiCrop];
933 
934 	    if (sbiDecode >= Base64.WHITE_SPACE_ENC) // White space, Equals
935 							// sign or better
936 	    {
937 		if (sbiDecode >= Base64.EQUALS_SIGN_ENC) {
938 		    b4[b4Posn++] = sbiCrop;
939 		    if (b4Posn > 3) {
940 			outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn,
941 				options);
942 			b4Posn = 0;
943 
944 			// If that was the equals sign, break out of 'for' loop
945 			if (sbiCrop == Base64.EQUALS_SIGN) {
946 			    break;
947 			}
948 		    } // end if: quartet built
949 
950 		} // end if: equals sign or better
951 
952 	    } // end if: white space, equals sign or better
953 	    else {
954 		System.err.println("Bad Base64 input character at " + i + ": "
955 			+ source[i] + "(decimal)");
956 		return null;
957 	    } // end else:
958 	} // each input character
959 
960 	byte[] out = new byte[outBuffPosn];
961 	System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
962 	return out;
963     } // end decode
964 
965     /**
966      * Decodes data from Base64 notation, automatically detecting
967      * gzip-compressed data and decompressing it.
968      * 
969      * @param s
970      *                the string to decode
971      * @return the decoded data
972      * @since 1.4
973      */
974     public static byte[] decode(String s) {
975 	return decode(s, Base64.NO_OPTIONS);
976     }
977 
978     /**
979      * Decodes data from Base64 notation, automatically detecting
980      * gzip-compressed data and decompressing it.
981      * 
982      * @param s
983      *                the string to decode
984      * @param options
985      *                encode options such as URL_SAFE
986      * @return the decoded data
987      * @since 1.4
988      */
989     public static byte[] decode(String s, int options) {
990 	byte[] bytes;
991 	try {
992 	    bytes = s.getBytes(Base64.PREFERRED_ENCODING);
993 	} // end try
994 	catch (java.io.UnsupportedEncodingException uee) {
995 	    bytes = s.getBytes();
996 	} // end catch
997 	// </change>
998 
999 	// Decode
1000 	bytes = decode(bytes, 0, bytes.length, options);
1001 
1002 	// Check to see if it's gzip-compressed
1003 	// GZIP Magic Two-Byte Number: 0x8b1f (35615)
1004 	if (bytes != null && bytes.length >= 4) {
1005 
1006 	    int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
1007 	    if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
1008 		java.io.ByteArrayInputStream bais = null;
1009 		java.util.zip.GZIPInputStream gzis = null;
1010 		java.io.ByteArrayOutputStream baos = null;
1011 		byte[] buffer = new byte[2048];
1012 		int length = 0;
1013 
1014 		try {
1015 		    baos = new java.io.ByteArrayOutputStream();
1016 		    bais = new java.io.ByteArrayInputStream(bytes);
1017 		    gzis = new java.util.zip.GZIPInputStream(bais);
1018 
1019 		    while ((length = gzis.read(buffer)) >= 0) {
1020 			baos.write(buffer, 0, length);
1021 		    } // end while: reading input
1022 
1023 		    // No error? Get new bytes.
1024 		    bytes = baos.toByteArray();
1025 
1026 		} // end try
1027 		catch (java.io.IOException e) {
1028 		    // Just return originally-decoded bytes
1029 		} // end catch
1030 		finally {
1031 		    try {
1032 			baos.close();
1033 		    } catch (Exception e) {
1034 		    }
1035 		    try {
1036 			gzis.close();
1037 		    } catch (Exception e) {
1038 		    }
1039 		    try {
1040 			bais.close();
1041 		    } catch (Exception e) {
1042 		    }
1043 		} // end finally
1044 
1045 	    } // end if: gzipped
1046 	} // end if: bytes.length >= 2
1047 
1048 	return bytes;
1049     } // end decode
1050 
1051     /**
1052      * Attempts to decode Base64 data and deserialize a Java Object within.
1053      * Returns <tt>null</tt> if there was an error.
1054      * 
1055      * @param encodedObject
1056      *                The Base64 data to decode
1057      * @return The decoded and deserialized object
1058      * @since 1.5
1059      */
1060     public static Object decodeToObject(String encodedObject) {
1061 	// Decode and gunzip if necessary
1062 	byte[] objBytes = decode(encodedObject);
1063 
1064 	java.io.ByteArrayInputStream bais = null;
1065 	java.io.ObjectInputStream ois = null;
1066 	Object obj = null;
1067 
1068 	try {
1069 	    bais = new java.io.ByteArrayInputStream(objBytes);
1070 	    ois = new java.io.ObjectInputStream(bais);
1071 
1072 	    obj = ois.readObject();
1073 	} // end try
1074 	catch (java.io.IOException e) {
1075 	    e.printStackTrace();
1076 	    obj = null;
1077 	} // end catch
1078 	catch (java.lang.ClassNotFoundException e) {
1079 	    e.printStackTrace();
1080 	    obj = null;
1081 	} // end catch
1082 	finally {
1083 	    try {
1084 		bais.close();
1085 	    } catch (Exception e) {
1086 	    }
1087 	    try {
1088 		ois.close();
1089 	    } catch (Exception e) {
1090 	    }
1091 	} // end finally
1092 
1093 	return obj;
1094     } // end decodeObject
1095 
1096     /**
1097      * Convenience method for encoding data to a file.
1098      * 
1099      * @param dataToEncode
1100      *                byte array of data to encode in base64 form
1101      * @param filename
1102      *                Filename for saving encoded data
1103      * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
1104      * 
1105      * @since 2.1
1106      */
1107     public static boolean encodeToFile(byte[] dataToEncode, String filename) {
1108 	boolean success = false;
1109 	Base64.OutputStream bos = null;
1110 	try {
1111 	    bos = new Base64.OutputStream(
1112 		    new java.io.FileOutputStream(filename), Base64.ENCODE);
1113 	    bos.write(dataToEncode);
1114 	    success = true;
1115 	} // end try
1116 	catch (java.io.IOException e) {
1117 
1118 	    success = false;
1119 	} // end catch: IOException
1120 	finally {
1121 	    try {
1122 		bos.close();
1123 	    } catch (Exception e) {
1124 	    }
1125 	} // end finally
1126 
1127 	return success;
1128     } // end encodeToFile
1129 
1130     /**
1131      * Convenience method for decoding data to a file.
1132      * 
1133      * @param dataToDecode
1134      *                Base64-encoded data as a string
1135      * @param filename
1136      *                Filename for saving decoded data
1137      * @return <tt>true</tt> if successful, <tt>false</tt> otherwise
1138      * 
1139      * @since 2.1
1140      */
1141     public static boolean decodeToFile(String dataToDecode, String filename) {
1142 	boolean success = false;
1143 	Base64.OutputStream bos = null;
1144 	try {
1145 	    bos = new Base64.OutputStream(
1146 		    new java.io.FileOutputStream(filename), Base64.DECODE);
1147 	    bos.write(dataToDecode.getBytes(Base64.PREFERRED_ENCODING));
1148 	    success = true;
1149 	} // end try
1150 	catch (java.io.IOException e) {
1151 	    success = false;
1152 	} // end catch: IOException
1153 	finally {
1154 	    try {
1155 		bos.close();
1156 	    } catch (Exception e) {
1157 	    }
1158 	} // end finally
1159 
1160 	return success;
1161     } // end decodeToFile
1162 
1163     /**
1164      * Convenience method for reading a base64-encoded file and decoding it.
1165      * 
1166      * @param filename
1167      *                Filename for reading encoded data
1168      * @return decoded byte array or null if unsuccessful
1169      * 
1170      * @since 2.1
1171      */
1172     public static byte[] decodeFromFile(String filename) {
1173 	byte[] decodedData = null;
1174 	Base64.InputStream bis = null;
1175 	try {
1176 	    // Set up some useful variables
1177 	    java.io.File file = new java.io.File(filename);
1178 	    byte[] buffer = null;
1179 	    int length = 0;
1180 	    int numBytes = 0;
1181 
1182 	    // Check for size of file
1183 	    if (file.length() > Integer.MAX_VALUE) {
1184 		System.err
1185 			.println("File is too big for this convenience method ("
1186 				+ file.length() + " bytes).");
1187 		return null;
1188 	    } // end if: file too big for int index
1189 	    buffer = new byte[(int) file.length()];
1190 
1191 	    // Open a stream
1192 	    bis = new Base64.InputStream(new java.io.BufferedInputStream(
1193 		    new java.io.FileInputStream(file)), Base64.DECODE);
1194 
1195 	    // Read until done
1196 	    while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
1197 		length += numBytes;
1198 	    }
1199 
1200 	    // Save in a variable to return
1201 	    decodedData = new byte[length];
1202 	    System.arraycopy(buffer, 0, decodedData, 0, length);
1203 
1204 	} // end try
1205 	catch (java.io.IOException e) {
1206 	    System.err.println("Error decoding from file " + filename);
1207 	} // end catch: IOException
1208 	finally {
1209 	    try {
1210 		bis.close();
1211 	    } catch (Exception e) {
1212 	    }
1213 	} // end finally
1214 
1215 	return decodedData;
1216     } // end decodeFromFile
1217 
1218     /**
1219      * Convenience method for reading a binary file and base64-encoding it.
1220      * 
1221      * @param filename
1222      *                Filename for reading binary data
1223      * @return base64-encoded string or null if unsuccessful
1224      * 
1225      * @since 2.1
1226      */
1227     public static String encodeFromFile(String filename) {
1228 	String encodedData = null;
1229 	Base64.InputStream bis = null;
1230 	try {
1231 	    // Set up some useful variables
1232 	    java.io.File file = new java.io.File(filename);
1233 	    byte[] buffer = new byte[Math.max((int) (file.length() * 1.4), 40)]; // Need
1234 										    // max()
1235 										    // for
1236 										    // math
1237 										    // on
1238 										    // small
1239 										    // files
1240 										    // (v2.2.1)
1241 	    int length = 0;
1242 	    int numBytes = 0;
1243 
1244 	    // Open a stream
1245 	    bis = new Base64.InputStream(new java.io.BufferedInputStream(
1246 		    new java.io.FileInputStream(file)), Base64.ENCODE);
1247 
1248 	    // Read until done
1249 	    while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
1250 		length += numBytes;
1251 	    }
1252 
1253 	    // Save in a variable to return
1254 	    encodedData = new String(buffer, 0, length,
1255 		    Base64.PREFERRED_ENCODING);
1256 
1257 	} // end try
1258 	catch (java.io.IOException e) {
1259 	    System.err.println("Error encoding from file " + filename);
1260 	} // end catch: IOException
1261 	finally {
1262 	    try {
1263 		bis.close();
1264 	    } catch (Exception e) {
1265 	    }
1266 	} // end finally
1267 
1268 	return encodedData;
1269     } // end encodeFromFile
1270 
1271     /**
1272      * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
1273      * 
1274      * @param infile
1275      *                Input file
1276      * @param outfile
1277      *                Output file
1278      * @since 2.2
1279      */
1280     public static void encodeFileToFile(String infile, String outfile) {
1281 	String encoded = Base64.encodeFromFile(infile);
1282 	java.io.OutputStream out = null;
1283 	try {
1284 	    out = new java.io.BufferedOutputStream(
1285 		    new java.io.FileOutputStream(outfile));
1286 	    out.write(encoded.getBytes("US-ASCII")); // Strict, 7-bit output.
1287 	} // end try
1288 	catch (java.io.IOException ex) {
1289 	    ex.printStackTrace();
1290 	} // end catch
1291 	finally {
1292 	    try {
1293 		out.close();
1294 	    } catch (Exception ex) {
1295 	    }
1296 	} // end finally
1297     } // end encodeFileToFile
1298 
1299     /**
1300      * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
1301      * 
1302      * @param infile
1303      *                Input file
1304      * @param outfile
1305      *                Output file
1306      * @since 2.2
1307      */
1308     public static void decodeFileToFile(String infile, String outfile) {
1309 	byte[] decoded = Base64.decodeFromFile(infile);
1310 	java.io.OutputStream out = null;
1311 	try {
1312 	    out = new java.io.BufferedOutputStream(
1313 		    new java.io.FileOutputStream(outfile));
1314 	    out.write(decoded);
1315 	} // end try
1316 	catch (java.io.IOException ex) {
1317 	    ex.printStackTrace();
1318 	} // end catch
1319 	finally {
1320 	    try {
1321 		out.close();
1322 	    } catch (Exception ex) {
1323 	    }
1324 	} // end finally
1325     } // end decodeFileToFile
1326 
1327     /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
1328 
1329     /**
1330      * A {@link Base64.InputStream} will read data from another
1331      * <tt>java.io.InputStream</tt>, given in the constructor, and
1332      * encode/decode to/from Base64 notation on the fly.
1333      * 
1334      * @see Base64
1335      * @since 1.3
1336      */
1337     public static class InputStream extends java.io.FilterInputStream {
1338 	private final boolean encode; // Encoding or decoding
1339 	private int position; // Current position in the buffer
1340 	private final byte[] buffer; // Small buffer holding converted data
1341 	private final int bufferLength; // Length of buffer (3 or 4)
1342 	private int numSigBytes; // Number of meaningful bytes in the buffer
1343 	private int lineLength;
1344 	private final boolean breakLines; // Break lines at less than 80
1345 					    // characters
1346 	private final int options; // Record options used to create the stream.
1347 	@SuppressWarnings("unused")
1348 	private final byte[] alphabet; // Local copies to avoid extra method
1349 					// calls
1350 	private final byte[] decodabet; // Local copies to avoid extra method
1351 					// calls
1352 
1353 	/**
1354 	 * Constructs a {@link Base64.InputStream} in DECODE mode.
1355 	 * 
1356 	 * @param in
1357 	 *                the <tt>java.io.InputStream</tt> from which to read
1358 	 *                data.
1359 	 * @since 1.3
1360 	 */
1361 	public InputStream(java.io.InputStream in) {
1362 	    this(in, Base64.DECODE);
1363 	} // end constructor
1364 
1365 	/**
1366 	 * Constructs a {@link Base64.InputStream} in either ENCODE or DECODE
1367 	 * mode.
1368 	 * <p>
1369 	 * Valid options:
1370 	 * 
1371 	 * <pre>
1372 	 *   ENCODE or DECODE: Encode or Decode as data is read.
1373 	 *   DONT_BREAK_LINES: don't break lines at 76 characters
1374 	 *     (only meaningful when encoding)
1375 	 *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
1376 	 * </pre>
1377 	 * 
1378 	 * <p>
1379 	 * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1380 	 * 
1381 	 * 
1382 	 * @param in
1383 	 *                the <tt>java.io.InputStream</tt> from which to read
1384 	 *                data.
1385 	 * @param options
1386 	 *                Specified options
1387 	 * @see Base64#ENCODE
1388 	 * @see Base64#DECODE
1389 	 * @see Base64#DONT_BREAK_LINES
1390 	 * @since 2.0
1391 	 */
1392 	public InputStream(java.io.InputStream in, int options) {
1393 	    super(in);
1394 	    this.breakLines = (options & Base64.DONT_BREAK_LINES) != Base64.DONT_BREAK_LINES;
1395 	    this.encode = (options & Base64.ENCODE) == Base64.ENCODE;
1396 	    this.bufferLength = this.encode ? 4 : 3;
1397 	    this.buffer = new byte[this.bufferLength];
1398 	    this.position = -1;
1399 	    this.lineLength = 0;
1400 	    this.options = options; // Record for later, mostly to determine
1401 				    // which alphabet to use
1402 	    this.alphabet = getAlphabet(options);
1403 	    this.decodabet = getDecodabet(options);
1404 	} // end constructor
1405 
1406 	/**
1407 	 * Reads enough of the input stream to convert to/from Base64 and
1408 	 * returns the next byte.
1409 	 * 
1410 	 * @return next byte
1411 	 * @since 1.3
1412 	 */
1413 	@Override
1414 	public int read() throws java.io.IOException {
1415 	    // Do we need to get data?
1416 	    if (this.position < 0) {
1417 		if (this.encode) {
1418 		    byte[] b3 = new byte[3];
1419 		    int numBinaryBytes = 0;
1420 		    for (int i = 0; i < 3; i++) {
1421 			try {
1422 			    int b = this.in.read();
1423 
1424 			    // If end of stream, b is -1.
1425 			    if (b >= 0) {
1426 				b3[i] = (byte) b;
1427 				numBinaryBytes++;
1428 			    } // end if: not end of stream
1429 
1430 			} // end try: read
1431 			catch (java.io.IOException e) {
1432 			    // Only a problem if we got no data at all.
1433 			    if (i == 0) {
1434 				throw e;
1435 			    }
1436 
1437 			} // end catch
1438 		    } // end for: each needed input byte
1439 
1440 		    if (numBinaryBytes > 0) {
1441 			encode3to4(b3, 0, numBinaryBytes, this.buffer, 0,
1442 				this.options);
1443 			this.position = 0;
1444 			this.numSigBytes = 4;
1445 		    } // end if: got data
1446 		    else {
1447 			return -1;
1448 		    } // end else
1449 		} // end if: encoding
1450 
1451 		// Else decoding
1452 		else {
1453 		    byte[] b4 = new byte[4];
1454 		    int i = 0;
1455 		    for (i = 0; i < 4; i++) {
1456 			// Read four "meaningful" bytes:
1457 			int b = 0;
1458 			do {
1459 			    b = this.in.read();
1460 			} while (b >= 0
1461 				&& this.decodabet[b & 0x7f] <= Base64.WHITE_SPACE_ENC);
1462 
1463 			if (b < 0) {
1464 			    break; // Reads a -1 if end of stream
1465 			}
1466 
1467 			b4[i] = (byte) b;
1468 		    } // end for: each needed input byte
1469 
1470 		    if (i == 4) {
1471 			this.numSigBytes = decode4to3(b4, 0, this.buffer, 0,
1472 				this.options);
1473 			this.position = 0;
1474 		    } // end if: got four characters
1475 		    else if (i == 0) {
1476 			return -1;
1477 		    } // end else if: also padded correctly
1478 		    else {
1479 			// Must have broken out from above.
1480 			throw new java.io.IOException(
1481 				"Improperly padded Base64 input.");
1482 		    } // end
1483 
1484 		} // end else: decode
1485 	    } // end else: get data
1486 
1487 	    // Got data?
1488 	    if (this.position >= 0) {
1489 		// End of relevant data?
1490 		if ( /* !encode && */this.position >= this.numSigBytes) {
1491 		    return -1;
1492 		}
1493 
1494 		if (this.encode && this.breakLines
1495 			&& this.lineLength >= Base64.MAX_LINE_LENGTH) {
1496 		    this.lineLength = 0;
1497 		    return '\n';
1498 		} // end if
1499 		else {
1500 		    this.lineLength++; // This isn't important when decoding
1501 		    // but throwing an extra "if" seems
1502 		    // just as wasteful.
1503 
1504 		    int b = this.buffer[this.position++];
1505 
1506 		    if (this.position >= this.bufferLength) {
1507 			this.position = -1;
1508 		    }
1509 
1510 		    return b & 0xFF; // This is how you "cast" a byte that's
1511 		    // intended to be unsigned.
1512 		} // end else
1513 	    } // end if: position >= 0
1514 
1515 	    // Else error
1516 	    else {
1517 		// When JDK1.4 is more accepted, use an assertion here.
1518 		throw new java.io.IOException(
1519 			"Error in Base64 code reading stream.");
1520 	    } // end else
1521 	} // end read
1522 
1523 	/**
1524 	 * Calls {@link #read()} repeatedly until the end of stream is reached
1525 	 * or <var>len</var> bytes are read. Returns number of bytes read into
1526 	 * array or -1 if end of stream is encountered.
1527 	 * 
1528 	 * @param dest
1529 	 *                array to hold values
1530 	 * @param off
1531 	 *                offset for array
1532 	 * @param len
1533 	 *                max number of bytes to read into array
1534 	 * @return bytes read into array or -1 if end of stream is encountered.
1535 	 * @since 1.3
1536 	 */
1537 	@Override
1538 	public int read(byte[] dest, int off, int len)
1539 		throws java.io.IOException {
1540 	    int i;
1541 	    int b;
1542 	    for (i = 0; i < len; i++) {
1543 		b = read();
1544 
1545 		// if( b < 0 && i == 0 )
1546 		// return -1;
1547 
1548 		if (b >= 0) {
1549 		    dest[off + i] = (byte) b;
1550 		} else if (i == 0) {
1551 		    return -1;
1552 		} else {
1553 		    break; // Out of 'for' loop
1554 		}
1555 	    } // end for: each byte read
1556 	    return i;
1557 	} // end read
1558 
1559     } // end inner class InputStream
1560 
1561     /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1562 
1563     /**
1564      * A {@link Base64.OutputStream} will write data to another
1565      * <tt>java.io.OutputStream</tt>, given in the constructor, and
1566      * encode/decode to/from Base64 notation on the fly.
1567      * 
1568      * @see Base64
1569      * @since 1.3
1570      */
1571     public static class OutputStream extends java.io.FilterOutputStream {
1572 	private final boolean encode;
1573 	private int position;
1574 	private byte[] buffer;
1575 	private final int bufferLength;
1576 	private int lineLength;
1577 	private final boolean breakLines;
1578 	private final byte[] b4; // Scratch used in a few places
1579 	private boolean suspendEncoding;
1580 	private final int options; // Record for later
1581 	@SuppressWarnings("unused")
1582 	private final byte[] alphabet; // Local copies to avoid extra method
1583 					// calls
1584 	private final byte[] decodabet; // Local copies to avoid extra method
1585 					// calls
1586 
1587 	/**
1588 	 * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1589 	 * 
1590 	 * @param out
1591 	 *                the <tt>java.io.OutputStream</tt> to which data will
1592 	 *                be written.
1593 	 * @since 1.3
1594 	 */
1595 	public OutputStream(java.io.OutputStream out) {
1596 	    this(out, Base64.ENCODE);
1597 	} // end constructor
1598 
1599 	/**
1600 	 * Constructs a {@link Base64.OutputStream} in either ENCODE or DECODE
1601 	 * mode.
1602 	 * <p>
1603 	 * Valid options:
1604 	 * 
1605 	 * <pre>
1606 	 *   ENCODE or DECODE: Encode or Decode as data is read.
1607 	 *   DONT_BREAK_LINES: don't break lines at 76 characters
1608 	 *     (only meaningful when encoding)
1609 	 *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
1610 	 * </pre>
1611 	 * 
1612 	 * <p>
1613 	 * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1614 	 * 
1615 	 * @param out
1616 	 *                the <tt>java.io.OutputStream</tt> to which data will
1617 	 *                be written.
1618 	 * @param options
1619 	 *                Specified options.
1620 	 * @see Base64#ENCODE
1621 	 * @see Base64#DECODE
1622 	 * @see Base64#DONT_BREAK_LINES
1623 	 * @since 1.3
1624 	 */
1625 	public OutputStream(java.io.OutputStream out, int options) {
1626 	    super(out);
1627 	    this.breakLines = (options & Base64.DONT_BREAK_LINES) != Base64.DONT_BREAK_LINES;
1628 	    this.encode = (options & Base64.ENCODE) == Base64.ENCODE;
1629 	    this.bufferLength = this.encode ? 3 : 4;
1630 	    this.buffer = new byte[this.bufferLength];
1631 	    this.position = 0;
1632 	    this.lineLength = 0;
1633 	    this.suspendEncoding = false;
1634 	    this.b4 = new byte[4];
1635 	    this.options = options;
1636 	    this.alphabet = getAlphabet(options);
1637 	    this.decodabet = getDecodabet(options);
1638 	} // end constructor
1639 
1640 	/**
1641 	 * Writes the byte to the output stream after converting to/from Base64
1642 	 * notation. When encoding, bytes are buffered three at a time before
1643 	 * the output stream actually gets a write() call. When decoding, bytes
1644 	 * are buffered four at a time.
1645 	 * 
1646 	 * @param theByte
1647 	 *                the byte to write
1648 	 * @since 1.3
1649 	 */
1650 	@Override
1651 	public void write(int theByte) throws java.io.IOException {
1652 	    // Encoding suspended?
1653 	    if (this.suspendEncoding) {
1654 		super.out.write(theByte);
1655 		return;
1656 	    } // end if: supsended
1657 
1658 	    // Encode?
1659 	    if (this.encode) {
1660 		this.buffer[this.position++] = (byte) theByte;
1661 		if (this.position >= this.bufferLength) // Enough to encode.
1662 		{
1663 		    this.out.write(encode3to4(this.b4, this.buffer,
1664 			    this.bufferLength, this.options));
1665 
1666 		    this.lineLength += 4;
1667 		    if (this.breakLines
1668 			    && this.lineLength >= Base64.MAX_LINE_LENGTH) {
1669 			this.out.write(Base64.NEW_LINE);
1670 			this.lineLength = 0;
1671 		    } // end if: end of line
1672 
1673 		    this.position = 0;
1674 		} // end if: enough to output
1675 	    } // end if: encoding
1676 
1677 	    // Else, Decoding
1678 	    else {
1679 		// Meaningful Base64 character?
1680 		if (this.decodabet[theByte & 0x7f] > Base64.WHITE_SPACE_ENC) {
1681 		    this.buffer[this.position++] = (byte) theByte;
1682 		    if (this.position >= this.bufferLength) // Enough to output.
1683 		    {
1684 			int len = Base64.decode4to3(this.buffer, 0, this.b4, 0,
1685 				this.options);
1686 			this.out.write(this.b4, 0, len);
1687 			// out.write( Base64.decode4to3( buffer ) );
1688 			this.position = 0;
1689 		    } // end if: enough to output
1690 		} // end if: meaningful base64 character
1691 		else if (this.decodabet[theByte & 0x7f] != Base64.WHITE_SPACE_ENC) {
1692 		    throw new java.io.IOException(
1693 			    "Invalid character in Base64 data.");
1694 		} // end else: not white space either
1695 	    } // end else: decoding
1696 	} // end write
1697 
1698 	/**
1699 	 * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are
1700 	 * written.
1701 	 * 
1702 	 * @param theBytes
1703 	 *                array from which to read bytes
1704 	 * @param off
1705 	 *                offset for array
1706 	 * @param len
1707 	 *                max number of bytes to read into array
1708 	 * @since 1.3
1709 	 */
1710 	@Override
1711 	public void write(byte[] theBytes, int off, int len)
1712 		throws java.io.IOException {
1713 	    // Encoding suspended?
1714 	    if (this.suspendEncoding) {
1715 		super.out.write(theBytes, off, len);
1716 		return;
1717 	    } // end if: supsended
1718 
1719 	    for (int i = 0; i < len; i++) {
1720 		write(theBytes[off + i]);
1721 	    } // end for: each byte written
1722 
1723 	} // end write
1724 
1725 	/**
1726 	 * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer
1727 	 * without closing the stream.
1728 	 */
1729 	public void flushBase64() throws java.io.IOException {
1730 	    if (this.position > 0) {
1731 		if (this.encode) {
1732 		    this.out.write(encode3to4(this.b4, this.buffer,
1733 			    this.position, this.options));
1734 		    this.position = 0;
1735 		} // end if: encoding
1736 		else {
1737 		    throw new java.io.IOException(
1738 			    "Base64 input not properly padded.");
1739 		} // end else: decoding
1740 	    } // end if: buffer partially full
1741 
1742 	} // end flush
1743 
1744 	/**
1745 	 * Flushes and closes (I think, in the superclass) the stream.
1746 	 * 
1747 	 * @since 1.3
1748 	 */
1749 	@Override
1750 	public void close() throws java.io.IOException {
1751 	    // 1. Ensure that pending characters are written
1752 	    flushBase64();
1753 
1754 	    // 2. Actually close the stream
1755 	    // Base class both flushes and closes.
1756 	    super.close();
1757 
1758 	    this.buffer = null;
1759 	    this.out = null;
1760 	} // end close
1761 
1762 	/**
1763 	 * Suspends encoding of the stream. May be helpful if you need to embed
1764 	 * a piece of base640-encoded data in a stream.
1765 	 * 
1766 	 * @since 1.5.1
1767 	 */
1768 	public void suspendEncoding() throws java.io.IOException {
1769 	    flushBase64();
1770 	    this.suspendEncoding = true;
1771 	} // end suspendEncoding
1772 
1773 	/**
1774 	 * Resumes encoding of the stream. May be helpful if you need to embed a
1775 	 * piece of base640-encoded data in a stream.
1776 	 * 
1777 	 * @since 1.5.1
1778 	 */
1779 	public void resumeEncoding() {
1780 	    this.suspendEncoding = false;
1781 	} // end resumeEncoding
1782 
1783     } // end inner class OutputStream
1784 
1785 } // end class Base64
1786