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