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