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