View Javadoc

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