View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.util;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.hadoop.hbase.classification.InterfaceAudience;
25  import org.apache.hadoop.hbase.classification.InterfaceStability;
26  
27  import java.io.BufferedInputStream;
28  import java.io.ByteArrayInputStream;
29  import java.io.ByteArrayOutputStream;
30  import java.io.File;
31  import java.io.FileInputStream;
32  import java.io.FilterInputStream;
33  import java.io.FilterOutputStream;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.io.OutputStream;
37  import java.io.UnsupportedEncodingException;
38  import java.util.zip.GZIPInputStream;
39  import java.util.zip.GZIPOutputStream;
40  
41  /**
42   * Encodes and decodes to and from Base64 notation.
43   *
44   * <p>
45   * Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.
46   * </p>
47   *
48   * <p>
49   * Change Log:
50   * </p>
51   * <ul>
52   *   <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug
53   *     when using very small files (~&lt; 40 bytes).</li>
54   *   <li>v2.2 - Added some helper methods for encoding/decoding directly from
55   *     one file to the next. Also added a main() method to support command
56   *     line encoding/decoding from one file to the next. Also added these
57   *     Base64 dialects:
58   *     <ol>
59   *       <li>The default is RFC3548 format.</li>
60   *       <li>Using Base64.URLSAFE generates URL and file name friendly format as
61   *         described in Section 4 of RFC3548.
62   *         http://www.faqs.org/rfcs/rfc3548.html</li>
63   *       <li>Using Base64.ORDERED generates URL and file name friendly format
64   *         that preserves lexical ordering as described in
65   *         http://www.faqs.org/qa/rfcc-1940.html</li>
66   *     </ol>
67   *     <p>
68   *     Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">
69   *     http://www.powerset.com/</a> for contributing the new Base64 dialects.
70   *   </li>
71   *
72   *   <li>v2.1 - Cleaned up javadoc comments and unused variables and methods.
73   *     Added some convenience methods for reading and writing to and from files.
74   *   </li>
75   *   <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on
76   *     systems with other encodings (like EBCDIC).</li>
77   *   <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
78   *     encoded data was a single byte.</li>
79   *   <li>v2.0 - I got rid of methods that used booleans to set options. Now
80   *     everything is more consolidated and cleaner. The code now detects when
81   *     data that's being decoded is gzip-compressed and will decompress it
82   *     automatically. Generally things are cleaner. You'll probably have to
83   *     change some method calls that you were making to support the new options
84   *     format (<tt>int</tt>s that you "OR" together).</li>
85   *   <li>v1.5.1 - Fixed bug when decompressing and decoding to a byte[] using
86   *     <tt>decode( String s, boolean gzipCompressed )</tt>. Added the ability to
87   *     "suspend" encoding in the Output Stream so you can turn on and off the
88   *     encoding if you need to embed base64 data in an otherwise "normal" stream
89   *     (like an XML file).</li>
90   *   <li>v1.5 - Output stream pases on flush() command but doesn't do anything
91   *     itself. This helps when using GZIP streams. Added the ability to
92   *     GZip-compress objects before encoding them.</li>
93   *   <li>v1.4 - Added helper methods to read/write files.</li>
94   *   <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
95   *   <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input
96   *     stream where last buffer being read, if not completely full, was not
97   *     returned.</li>
98   *   <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the
99   *     wrong time.</li>
100  *   <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
101  * </ul>
102  *
103  * <p>
104  * I am placing this code in the Public Domain. Do with it as you will. This
105  * software comes with no guarantees or warranties but with plenty of
106  * well-wishing instead!
107  * <p>
108  * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
109  * periodically to check for updates or to contribute improvements.
110  * <p>
111  * author: Robert Harder, rob@iharder.net
112  * <br>
113  * version: 2.2.1
114  */
115 @InterfaceAudience.Private
116 @InterfaceStability.Stable
117 public class Base64 {
118 
119   /* ******** P U B L I C   F I E L D S ******** */
120 
121   /** No options specified. Value is zero. */
122   public final static int NO_OPTIONS = 0;
123 
124   /** Specify encoding. */
125   public final static int ENCODE = 1;
126 
127   /** Specify decoding. */
128   public final static int DECODE = 0;
129 
130   /** Specify that data should be gzip-compressed. */
131   public final static int GZIP = 2;
132 
133   /** Don't break lines when encoding (violates strict Base64 specification) */
134   public final static int DONT_BREAK_LINES = 8;
135 
136   /**
137    * Encode using Base64-like encoding that is URL and Filename safe as
138    * described in Section 4 of RFC3548:
139    * <a href="http://www.faqs.org/rfcs/rfc3548.html">
140    * http://www.faqs.org/rfcs/rfc3548.html</a>.
141    * It is important to note that data encoded this way is <em>not</em>
142    * officially valid Base64, or at the very least should not be called Base64
143    * without also specifying that is was encoded using the URL and
144    * Filename safe dialect.
145    */
146   public final static int URL_SAFE = 16;
147 
148   /**
149    * Encode using the special "ordered" dialect of Base64 described here:
150    * <a href="http://www.faqs.org/qa/rfcc-1940.html">
151    * http://www.faqs.org/qa/rfcc-1940.html</a>.
152    */
153   public final static int ORDERED = 32;
154 
155   /* ******** P R I V A T E   F I E L D S ******** */
156 
157   private static final Log LOG = LogFactory.getLog(Base64.class);
158 
159   /** Maximum line length (76) of Base64 output. */
160   private final static int MAX_LINE_LENGTH = 76;
161 
162   /** The equals sign (=) as a byte. */
163   private final static byte EQUALS_SIGN = (byte) '=';
164 
165   /** The new line character (\n) as a byte. */
166   private final static byte NEW_LINE = (byte) '\n';
167 
168   /** Preferred encoding. */
169   private final static String PREFERRED_ENCODING = "UTF-8";
170 
171   private final static byte WHITE_SPACE_ENC = -5; // Indicates white space
172   private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign
173 
174   /* ******** S T A N D A R D   B A S E 6 4   A L P H A B E T ******** */
175 
176   /** The 64 valid Base64 values. */
177 
178   /*
179    * Host platform may be something funny like EBCDIC, so we hardcode these
180    * values.
181    */
182   private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B',
183     (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
184     (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
185     (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
186     (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
187     (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
188     (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
189     (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
190     (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
191     (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
192     (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
193     (byte) '+', (byte) '/'
194   };
195 
196   /**
197    * Translates a Base64 value to either its 6-bit reconstruction value or a
198    * negative number indicating some other meaning.
199    */
200   private final static byte[] _STANDARD_DECODABET = {
201     -9, -9, -9, -9, -9, -9, -9, -9, -9,             // Decimal 0 - 8
202     -5, -5,                                         // Whitespace: Tab, Newline
203     -9, -9,                                         // Decimal 11 - 12
204     -5,                                             // Whitespace: Return
205     -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
206     -9, -9, -9, -9, -9,                             // Decimal 27 - 31
207     -5,                                             // Whitespace: Space
208     -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,         // Decimal 33 - 42
209     62,                                             // Plus sign at decimal 43
210     -9, -9, -9,                                     // Decimal 44 - 46
211     63,                                             // Slash at decimal 47
212     52, 53, 54, 55, 56, 57, 58, 59, 60, 61,         // Numbers zero - nine
213     -9, -9, -9,                                     // Decimal 58 - 60
214     -1,                                             // Equals sign at decimal 61
215     -9, -9, -9,                                     // Decimal 62 - 64
216     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,   // Letters 'A' - 'N'
217     14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' - 'Z'
218     -9, -9, -9, -9, -9, -9,                         // Decimal 91 - 96
219     26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' - 'm'
220     39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' -'z'
221     -9, -9, -9, -9                                  // Decimal 123 - 126
222   };
223 
224   /* ******** U R L   S A F E   B A S E 6 4   A L P H A B E T ******** */
225 
226   /**
227    * Used in the URL and Filename safe dialect described in Section 4 of RFC3548
228    * <a href="http://www.faqs.org/rfcs/rfc3548.html">
229    * http://www.faqs.org/rfcs/rfc3548.html</a>.
230    * Notice that the last two bytes become "hyphen" and "underscore" instead of
231    * "plus" and "slash."
232    */
233   private final static byte[] _URL_SAFE_ALPHABET = { (byte) 'A', (byte) 'B',
234     (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
235     (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
236     (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
237     (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
238     (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
239     (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
240     (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
241     (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
242     (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
243     (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9',
244     (byte) '-', (byte) '_'
245   };
246 
247   /**
248    * Used in decoding URL and Filename safe dialects of Base64.
249    */
250   private final static byte[] _URL_SAFE_DECODABET = {
251     -9, -9, -9, -9, -9, -9, -9, -9, -9,                 // Decimal 0 - 8
252     -5, -5,                                             // Whitespace: Tab, Newline
253     -9, -9,                                             // Decimal 11 - 12
254     -5,                                                 // Whitespace: Return
255     -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
256     -9, -9, -9, -9, -9,                                 // Decimal 27 - 31
257     -5,                                                 // Whitespace: Space
258     -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,             // Decimal 33 - 42
259     -9,                                                 // Plus sign at 43
260     -9,                                                 // Decimal 44
261     62,                                                 // Minus sign at 45
262     -9,                                                 // Decimal 46
263     -9,                                                 // Slash at 47
264     52, 53, 54, 55, 56, 57, 58, 59, 60, 61,             // Numbers 0 - 9
265     -9, -9, -9,                                         // Decimal 58 - 60
266     -1,                                                 // Equals sign at 61
267     -9, -9, -9,                                         // Decimal 62 - 64
268     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,       // Letters 'A' - 'N'
269     14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,     // Letters 'O' - 'Z'
270     -9, -9, -9, -9,                                     // Decimal 91 - 94
271     63,                                                 // Underscore at 95
272     -9,                                                 // Decimal 96
273     26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' - 'm'
274     39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' - 'z'
275     -9, -9, -9, -9                                      // Decimal 123 - 126
276   };
277 
278   /* ******** O R D E R E D   B A S E 6 4   A L P H A B E T ******** */
279 
280   /**
281    * In addition to being URL and file name friendly, this encoding preserves
282    * the sort order of encoded values. Whatever is input, be it string or
283    * just an array of bytes, when you use this encoding, the encoded value sorts
284    * exactly the same as the input value. It is described in the RFC change
285    * request: <a href="http://www.faqs.org/qa/rfcc-1940.html">
286    * http://www.faqs.org/qa/rfcc-1940.html</a>.
287    *
288    * It replaces "plus" and "slash" with "hyphen" and "underscore" and
289    * rearranges the alphabet so that the characters are in their natural sort
290    * order.
291    */
292   private final static byte[] _ORDERED_ALPHABET = { (byte) '-', (byte) '0',
293     (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
294     (byte) '7', (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C',
295     (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I',
296     (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O',
297     (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
298     (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) '_',
299     (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
300     (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
301     (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r',
302     (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
303     (byte) 'y', (byte) 'z'
304   };
305 
306   /**
307    * Used in decoding the "ordered" dialect of Base64.
308    */
309   private final static byte[] _ORDERED_DECODABET = {
310     -9, -9, -9, -9, -9, -9, -9, -9, -9,                 // Decimal 0 - 8
311     -5, -5,                                             // Whitespace: Tab, Newline
312     -9, -9,                                             // Decimal 11 - 12
313     -5,                                                 // Whitespace: Return
314     -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
315     -9, -9, -9, -9, -9,                                 // Decimal 27 - 31
316     -5,                                                 // Whitespace: Space
317     -9, -9, -9, -9, -9, -9, -9, -9, -9, -9,             // Decimal 33 - 42
318     -9,                                                 // Plus sign at 43
319     -9,                                                 // Decimal 44
320     0,                                                  // Minus sign at 45
321     -9,                                                 // Decimal 46
322     -9,                                                 // Slash at decimal 47
323     1, 2, 3, 4, 5, 6, 7, 8, 9, 10,                      // Numbers 0 - 9
324     -9, -9, -9,                                         // Decimal 58 - 60
325     -1,                                                 // Equals sign at 61
326     -9, -9, -9,                                         // Decimal 62 - 64
327     11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' - 'M'
328     24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' - 'Z'
329     -9, -9, -9, -9,                                     // Decimal 91 - 94
330     37,                                                 // Underscore at 95
331     -9,                                                 // Decimal 96
332     38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' - 'm'
333     51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' - 'z'
334     -9, -9, -9, -9                                      // Decimal 123 - 126
335   };
336 
337   /* ******** D E T E R M I N E   W H I C H   A L H A B E T ******** */
338 
339   /**
340    * Returns one of the _SOMETHING_ALPHABET byte arrays depending on the options
341    * specified. It's possible, though silly, to specify ORDERED and URLSAFE in
342    * which case one of them will be picked, though there is no guarantee as to
343    * which one will be picked.
344    *
345    * @param options URL_SAFE or ORDERED
346    * @return alphabet array to use
347    */
348   protected static byte[] getAlphabet(int options) {
349     if ((options & URL_SAFE) == URL_SAFE) {
350       return _URL_SAFE_ALPHABET;
351 
352     } else if ((options & ORDERED) == ORDERED) {
353       return _ORDERED_ALPHABET;
354 
355     } else {
356       return _STANDARD_ALPHABET;
357     }
358   } // end getAlphabet
359 
360   /**
361    * Returns one of the _SOMETHING_DECODABET byte arrays depending on the
362    * options specified. It's possible, though silly, to specify ORDERED and
363    * URL_SAFE in which case one of them will be picked, though there is no
364    * guarantee as to which one will be picked.
365    * @param options URL_SAFE or ORDERED
366    * @return alphabet array to use
367    */
368   protected static byte[] getDecodabet(int options) {
369     if ((options & URL_SAFE) == URL_SAFE) {
370       return _URL_SAFE_DECODABET;
371 
372     } else if ((options & ORDERED) == ORDERED) {
373       return _ORDERED_DECODABET;
374 
375     } else {
376       return _STANDARD_DECODABET;
377     }
378   } // end getDecodabet
379 
380   /** Defeats instantiation. */
381   private Base64() {}
382 
383   /* ******** E N C O D I N G   M E T H O D S ******** */
384 
385   /**
386    * Encodes up to the first three bytes of array <var>threeBytes</var> and
387    * returns a four-byte array in Base64 notation. The actual number of
388    * significant bytes in your array is given by <var>numSigBytes</var>. The
389    * array <var>threeBytes</var> needs only be as big as <var>numSigBytes</var>.
390    * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
391    *
392    * @param b4 A reusable byte array to reduce array instantiation
393    * @param threeBytes the array to convert
394    * @param numSigBytes the number of significant bytes in your array
395    * @param options options for get alphabet
396    * @return four byte array in Base64 notation.
397    * @since 1.5.1
398    */
399   protected static byte[] encode3to4(byte[] b4, byte[] threeBytes,
400       int numSigBytes, int options) {
401     encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
402     return b4;
403   } // end encode3to4
404 
405   /**
406    * Encodes up to three bytes of the array <var>source</var> and writes the
407    * resulting four Base64 bytes to <var>destination</var>. The source and
408    * destination arrays can be manipulated anywhere along their length by
409    * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
410    * does not check to make sure your arrays are large enough to accomodate
411    * <var>srcOffset</var> + 3 for the <var>source</var> array or
412    * <var>destOffset</var> + 4 for the <var>destination</var> array. The
413    * actual number of significant bytes in your array is given by
414    * <var>numSigBytes</var>.
415    * <p>
416    * This is the lowest level of the encoding methods with all possible
417    * parameters.
418    *
419    * @param source the array to convert
420    * @param srcOffset the index where conversion begins
421    * @param numSigBytes the number of significant bytes in your array
422    * @param destination the array to hold the conversion
423    * @param destOffset the index where output will be put
424    * @param options options for get alphabet
425    * @return the <var>destination</var> array
426    * @since 1.3
427    */
428   protected static byte[] encode3to4(byte[] source, int srcOffset,
429       int numSigBytes, byte[] destination, int destOffset, int options) {
430     byte[] ALPHABET = getAlphabet(options);
431 
432     //           1         2         3
433     // 01234567890123456789012345678901 Bit position
434     // --------000000001111111122222222 Array position from threeBytes
435     // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
436     //          >>18  >>12  >> 6  >> 0  Right shift necessary
437     //                0x3f  0x3f  0x3f  Additional AND
438 
439     // Create buffer with zero-padding if there are only one or two
440     // significant bytes passed in the array.
441     // We have to shift left 24 in order to flush out the 1's that appear
442     // when Java treats a value as negative that is cast from a byte to an int.
443     int inBuff =
444         (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
445             | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
446             | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
447 
448     switch (numSigBytes) {
449     case 3:
450       destination[destOffset] = ALPHABET[(inBuff >>> 18)];
451       destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
452       destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
453       destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
454       return destination;
455 
456     case 2:
457       destination[destOffset] = ALPHABET[(inBuff >>> 18)];
458       destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
459       destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
460       destination[destOffset + 3] = EQUALS_SIGN;
461       return destination;
462 
463     case 1:
464       destination[destOffset] = ALPHABET[(inBuff >>> 18)];
465       destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
466       destination[destOffset + 2] = EQUALS_SIGN;
467       destination[destOffset + 3] = EQUALS_SIGN;
468       return destination;
469 
470     default:
471       return destination;
472     } // end switch
473   } // end encode3to4
474 
475   /**
476    * Encodes a byte array into Base64 notation. Does not GZip-compress data.
477    *
478    * @param source The data to convert
479    * @return encoded byte array
480    * @since 1.4
481    */
482   public static String encodeBytes(byte[] source) {
483     return encodeBytes(source, 0, source.length, NO_OPTIONS);
484   } // end encodeBytes
485 
486   /**
487    * Encodes a byte array into Base64 notation.
488    * <p>
489    * Valid options:
490    * <ul>
491    *   <li>GZIP: gzip-compresses object before encoding it.</li>
492    *   <li>DONT_BREAK_LINES: don't break lines at 76 characters. <i>Note:
493    *     Technically, this makes your encoding non-compliant.</i></li>
494    * </ul>
495    *
496    * <p>
497    * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
498    * <p>
499    * Example:
500    * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
501    *
502    * @param source The data to convert
503    * @param options Specified options
504    * @see Base64#GZIP
505    * @see Base64#DONT_BREAK_LINES
506    * @see Base64#URL_SAFE
507    * @see Base64#ORDERED
508    * @return encoded byte array
509    * @since 2.0
510    */
511   public static String encodeBytes(byte[] source, int options) {
512     return encodeBytes(source, 0, source.length, options);
513   } // end encodeBytes
514 
515   /**
516    * Encodes a byte array into Base64 notation.
517    * <p>
518    * Valid options:
519    * <ul>
520    *   <li>GZIP: gzip-compresses object before encoding it.</li>
521    *   <li>DONT_BREAK_LINES: don't break lines at 76 characters. <i>Note:
522    *     Technically, this makes your encoding non-compliant.</i></li>
523    * </ul>
524    *
525    * <p>
526    * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
527    * <p>
528    * Example:
529    * <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
530    *
531    * @param source The data to convert
532    * @param off Offset in array where conversion should begin
533    * @param len Length of data to convert
534    * @param options Specified options
535    * @see Base64#GZIP
536    * @see Base64#DONT_BREAK_LINES
537    * @see Base64#URL_SAFE
538    * @see Base64#ORDERED
539    * @return encoded byte array
540    * @since 2.0
541    */
542   public static String encodeBytes(byte[] source, int off, int len, int options) {
543     if ((options & GZIP) == GZIP) {                             // Compress?
544       // GZip -> Base64 -> ByteArray
545       ByteArrayOutputStream baos = new ByteArrayOutputStream();
546       GZIPOutputStream gzos = null;
547 
548       try {
549         gzos =
550           new GZIPOutputStream(new Base64OutputStream(baos, ENCODE | options));
551 
552         gzos.write(source, off, len);
553         gzos.close();
554         gzos = null;
555         return new String(baos.toByteArray(), PREFERRED_ENCODING);
556 
557       } catch (UnsupportedEncodingException uue) {
558         return new String(baos.toByteArray());
559 
560       } catch (IOException e) {
561         LOG.error("error encoding byte array", e);
562         return null;
563 
564       } finally {
565         if (gzos != null) {
566           try {
567             gzos.close();
568           } catch (Exception e) {
569             LOG.error("error closing GZIPOutputStream", e);
570           }
571         }
572         try {
573           baos.close();
574         } catch (Exception e) {
575           LOG.error("error closing ByteArrayOutputStream", e);
576         }
577       } // end finally
578 
579     } // end Compress
580 
581     // Don't compress. Better not to use streams at all then.
582 
583     boolean breakLines = ((options & DONT_BREAK_LINES) == 0);
584 
585     int len43 = len * 4 / 3;
586     byte[] outBuff =
587       new byte[(len43)                                          // Main 4:3
588                + ((len % 3) > 0 ? 4 : 0)                        // padding
589                + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
590     int d = 0;
591     int e = 0;
592     int len2 = len - 2;
593     int lineLength = 0;
594     for (; d < len2; d += 3, e += 4) {
595       encode3to4(source, d + off, 3, outBuff, e, options);
596 
597       lineLength += 4;
598       if (breakLines && lineLength == MAX_LINE_LENGTH) {
599         outBuff[e + 4] = NEW_LINE;
600         e++;
601         lineLength = 0;
602       } // end if: end of line
603     } // end for: each piece of array
604 
605     if (d < len) {
606       encode3to4(source, d + off, len - d, outBuff, e, options);
607       e += 4;
608     } // end if: some padding needed
609 
610     // Return value according to relevant encoding.
611     try {
612       return new String(outBuff, 0, e, PREFERRED_ENCODING);
613 
614     } catch (UnsupportedEncodingException uue) {
615       return new String(outBuff, 0, e);
616     }
617   } // end encodeBytes
618 
619   /* ******** D E C O D I N G   M E T H O D S ******** */
620 
621   /**
622    * Decodes four bytes from array <var>source</var> and writes the resulting
623    * bytes (up to three of them) to <var>destination</var>. The source and
624    * destination arrays can be manipulated anywhere along their length by
625    * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
626    * does not check to make sure your arrays are large enough to accomodate
627    * <var>srcOffset</var> + 4 for the <var>source</var> array or
628    * <var>destOffset</var> + 3 for the <var>destination</var> array. This
629    * method returns the actual number of bytes that were converted from the
630    * Base64 encoding.
631    * <p>
632    * This is the lowest level of the decoding methods with all possible
633    * parameters.
634    * </p>
635    *
636    * @param source the array to convert
637    * @param srcOffset the index where conversion begins
638    * @param destination the array to hold the conversion
639    * @param destOffset the index where output will be put
640    * @param options options for getDecoabet
641    * @see Base64#URL_SAFE
642    * @see Base64#ORDERED
643    * @return the number of decoded bytes converted
644    * @since 1.3
645    */
646   @SuppressWarnings({"ConstantConditions"})
647   protected static int decode4to3(byte[] source, int srcOffset,
648       byte[] destination, int destOffset, int options) {
649     byte[] DECODABET = getDecodabet(options);
650 
651     if (source[srcOffset + 2] == EQUALS_SIGN) {                 // Example: Dk==
652       // Two ways to do the same thing. Don't know which way I like best.
653       // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
654       // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
655       int outBuff =
656           ((DECODABET[source[srcOffset]] & 0xFF) << 18)
657               | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
658 
659       destination[destOffset] = (byte) (outBuff >>> 16);
660       return 1;
661 
662     } else if (source[srcOffset + 3] == EQUALS_SIGN) {          // Example: DkL=
663       // Two ways to do the same thing. Don't know which way I like best.
664       // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
665       // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
666       // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
667       int outBuff =
668           ((DECODABET[source[srcOffset]] & 0xFF) << 18)
669               | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
670               | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
671 
672       destination[destOffset] = (byte) (outBuff >>> 16);
673       destination[destOffset + 1] = (byte) (outBuff >>> 8);
674       return 2;
675 
676     } else {                                                    // Example: DkLE
677       try {
678         // Two ways to do the same thing. Don't know which way I like best.
679         // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
680         // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
681         // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
682         // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
683         int outBuff =
684             ((DECODABET[source[srcOffset]] & 0xFF) << 18)
685                 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
686                 | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
687                 | ((DECODABET[source[srcOffset + 3]] & 0xFF));
688 
689         destination[destOffset] = (byte) (outBuff >> 16);
690         destination[destOffset + 1] = (byte) (outBuff >> 8);
691         destination[destOffset + 2] = (byte) (outBuff);
692 
693         return 3;
694 
695       } catch (Exception e) {
696         LOG.error("error decoding bytes at " + source[srcOffset] + ": " +
697             (DECODABET[source[srcOffset]]) + ", " + source[srcOffset + 1] +
698             ": " + (DECODABET[source[srcOffset + 1]]) + ", " +
699             source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]]) +
700             ", " + source[srcOffset + 3] + ": " +
701             (DECODABET[source[srcOffset + 3]]), e);
702         return -1;
703       } // end catch
704     }
705   } // end decodeToBytes
706 
707   /**
708    * Very low-level access to decoding ASCII characters in the form of a byte
709    * array. Does not support automatically gunzipping or any other "fancy"
710    * features.
711    *
712    * @param source The Base64 encoded data
713    * @param off The offset of where to begin decoding
714    * @param len The length of characters to decode
715    * @param options options for getDecodabet
716    * @see Base64#URL_SAFE
717    * @see Base64#ORDERED
718    * @return decoded data
719    * @since 1.3
720    */
721   public static byte[] decode(byte[] source, int off, int len, int options) {
722     byte[] DECODABET = getDecodabet(options);
723 
724     int len34 = len * 3 / 4;
725     byte[] outBuff = new byte[len34];           // Upper limit on size of output
726     int outBuffPosn = 0;
727 
728     byte[] b4 = new byte[4];
729     int b4Posn = 0;
730     int i;
731     byte sbiCrop;
732     byte sbiDecode;
733     for (i = off; i < off + len; i++) {
734       sbiCrop = (byte) (source[i] & 0x7f);      // Only the low seven bits
735       sbiDecode = DECODABET[sbiCrop];
736 
737       if (sbiDecode >= WHITE_SPACE_ENC) {       // Whitespace, Equals or better
738         if (sbiDecode >= EQUALS_SIGN_ENC) {     // Equals or better
739           b4[b4Posn++] = sbiCrop;
740           if (b4Posn > 3) {
741             outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options);
742             b4Posn = 0;
743 
744             // If that was the equals sign, break out of 'for' loop
745             if (sbiCrop == EQUALS_SIGN)
746               break;
747           } // end if: quartet built
748         } // end if: equals sign or better
749       } else {
750         LOG.error("Bad Base64 input character at " + i + ": " + source[i] +
751             "(decimal)");
752         return null;
753       } // end else:
754     } // each input character
755 
756     byte[] out = new byte[outBuffPosn];
757     System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
758     return out;
759   } // end decode
760 
761   /**
762    * Decodes data from Base64 notation, automatically detecting gzip-compressed
763    * data and decompressing it.
764    *
765    * @param s the string to decode
766    * @return the decoded data
767    * @since 1.4
768    */
769   public static byte[] decode(String s) {
770     return decode(s, NO_OPTIONS);
771   }
772 
773   /**
774    * Decodes data from Base64 notation, automatically detecting gzip-compressed
775    * data and decompressing it.
776    *
777    * @param s the string to decode
778    * @param options options for decode
779    * @see Base64#URL_SAFE
780    * @see Base64#ORDERED
781    * @return the decoded data
782    * @since 1.4
783    */
784   public static byte[] decode(String s, int options) {
785     byte[] bytes;
786     try {
787       bytes = s.getBytes(PREFERRED_ENCODING);
788 
789     } catch (UnsupportedEncodingException uee) {
790       bytes = s.getBytes();
791     } // end catch
792 
793     // Decode
794 
795     bytes = decode(bytes, 0, bytes.length, options);
796 
797     // Check to see if it's gzip-compressed
798     // GZIP Magic Two-Byte Number: 0x8b1f (35615)
799 
800     if (bytes != null && bytes.length >= 4) {
801       int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
802       if (GZIPInputStream.GZIP_MAGIC == head) {
803         GZIPInputStream gzis = null;
804         ByteArrayOutputStream baos = new ByteArrayOutputStream();
805         try {
806           gzis = new GZIPInputStream(new ByteArrayInputStream(bytes));
807 
808           byte[] buffer = new byte[2048];
809           for (int length; (length = gzis.read(buffer)) >= 0; ) {
810             baos.write(buffer, 0, length);
811           } // end while: reading input
812 
813           // No error? Get new bytes.
814           bytes = baos.toByteArray();
815 
816         } catch (IOException e) {
817           // Just return originally-decoded bytes
818 
819         } finally {
820           try {
821             baos.close();
822           } catch (Exception e) {
823             LOG.error("error closing ByteArrayOutputStream", e);
824           }
825           if (gzis != null) {
826             try {
827               gzis.close();
828             } catch (Exception e) {
829               LOG.error("error closing GZIPInputStream", e);
830             }
831           }
832         } // end finally
833       } // end if: gzipped
834     } // end if: bytes.length >= 2
835 
836     return bytes;
837   } // end decode
838 
839   /**
840    * Convenience method for reading a base64-encoded file and decoding it.
841    *
842    * @param filename Filename for reading encoded data
843    * @return decoded byte array or null if unsuccessful
844    *
845    * @since 2.1
846    */
847   public static byte[] decodeFromFile(String filename) {
848     byte[] decodedData = null;
849     BufferedInputStream bufferedInputStream = null;
850     FileInputStream fileInputStream = null;
851     Base64InputStream base64InputStream = null;
852     try {
853       File file = new File(filename);
854       byte[] buffer;
855 
856       // Check the size of file
857       if (file.length() > Integer.MAX_VALUE) {
858         LOG.fatal("File is too big for this convenience method (" +
859             file.length() + " bytes).");
860         return null;
861       } // end if: file too big for int index
862 
863       buffer = new byte[(int) file.length()];
864 
865       // Open a stream
866       fileInputStream = new FileInputStream(file);
867       bufferedInputStream = new BufferedInputStream(fileInputStream);
868       base64InputStream = new Base64InputStream(bufferedInputStream, DECODE);
869 
870       // Read until done
871 
872       int length = 0;
873       for (int numBytes; (numBytes = base64InputStream.read(buffer, length, 4096)) >= 0; ) {
874         length += numBytes;
875       }
876 
877       // Save in a variable to return
878 
879       decodedData = new byte[length];
880       System.arraycopy(buffer, 0, decodedData, 0, length);
881 
882     } catch (IOException e) {
883       LOG.error("Error decoding from file " + filename, e);
884 
885     } finally {
886       if (fileInputStream != null) {
887         try {
888           fileInputStream.close();
889         } catch (Exception e) {
890           LOG.error("error closing FileInputStream", e);
891         }
892       }
893       if (bufferedInputStream != null) {
894         try {
895           bufferedInputStream.close();
896         } catch (Exception e) {
897           LOG.error("error closing BufferedInputStream", e);
898         }
899       }
900       if (base64InputStream != null) {
901         try {
902           base64InputStream.close();
903         } catch (Exception e) {
904           LOG.error("error closing Base64InputStream", e);
905         }
906       }
907     } // end finally
908 
909     return decodedData;
910   } // end decodeFromFile
911 
912   /**
913    * Convenience method for reading a binary file and base64-encoding it.
914    *
915    * @param filename Filename for reading binary data
916    * @return base64-encoded string or null if unsuccessful
917    *
918    * @since 2.1
919    */
920   public static String encodeFromFile(String filename) {
921     String encodedData = null;
922     FileInputStream fileInputStream = null;
923     BufferedInputStream bufferedInputStream = null;
924     Base64InputStream base64InputStream = null;
925 
926     try {
927       File file = new File(filename);
928 
929       // Need max() for math on small files (v2.2.1)
930 
931       byte[] buffer = new byte[Math.max((int) (file.length() * 1.4), 40)];
932 
933       // Open a stream
934 
935       fileInputStream = new FileInputStream(file);
936       bufferedInputStream = new BufferedInputStream(fileInputStream);
937       base64InputStream = new Base64InputStream(bufferedInputStream, ENCODE);
938 
939       // Read until done
940       int length = 0;
941       for (int numBytes; (numBytes = base64InputStream.read(buffer, length, 4096)) >= 0; ) {
942         length += numBytes;
943       }
944 
945       // Save in a variable to return
946 
947       encodedData = new String(buffer, 0, length, PREFERRED_ENCODING);
948 
949     } catch (IOException e) {
950       LOG.error("Error encoding from file " + filename, e);
951 
952     } finally {
953       // Can't leak exceptions but still need to clean things up.
954       if (fileInputStream != null) {
955         try {
956           fileInputStream.close();
957         } catch (Exception e) {
958           LOG.error("error closing FileInputStream", e);
959         }
960       }
961       if (bufferedInputStream != null) {
962         try {
963           bufferedInputStream.close();
964         } catch (Exception e) {
965           LOG.error("error closing BufferedInputStream", e);
966         }
967       }
968       if (base64InputStream != null) {
969         try {
970           base64InputStream.close();
971         } catch (Exception e) {
972           LOG.error("error closing Base64InputStream", e);
973         }
974       }
975     } // end finally
976 
977     return encodedData;
978   } // end encodeFromFile
979 
980   /* ******** I N N E R   C L A S S   I N P U T S T R E A M ******** */
981 
982   /**
983    * A {@link Base64.Base64InputStream} will read data from another
984    * <tt>InputStream</tt>, given in the constructor, and
985    * encode/decode to/from Base64 notation on the fly.
986    *
987    * @see Base64
988    * @since 1.3
989    */
990   @InterfaceAudience.Private
991   @InterfaceStability.Stable
992   public static class Base64InputStream extends FilterInputStream {
993     private boolean encode;                     // Encoding or decoding
994     private int position;                       // Current position in the buffer
995     private byte[] buffer;                      // Buffer holding converted data
996     private int bufferLength;                   // Length of buffer (3 or 4)
997     private int numSigBytes;                    // Meaningful bytes in the buffer
998     private int lineLength;
999     private boolean breakLines;                 // Break lines at < 80 characters
1000     private int options;                        // Record options
1001     private byte[] decodabet;                   // Local copy avoids method calls
1002 
1003     /**
1004      * Constructs a {@link Base64.Base64InputStream} in either ENCODE or DECODE mode.
1005      * <p>
1006      * Valid options:
1007      *
1008      * <pre>
1009      *   ENCODE or DECODE: Encode or Decode as data is read.
1010      *   DONT_BREAK_LINES: don't break lines at 76 characters
1011      *     (only meaningful when encoding)
1012      *     &lt;i&gt;Note: Technically, this makes your encoding non-compliant.&lt;/i&gt;
1013      * </pre>
1014      *
1015      * <p>
1016      * Example: <code>new Base64.Base64InputStream( in, Base64.DECODE )</code>
1017      *
1018      *
1019      * @param in the <tt>InputStream</tt> from which to read data.
1020      * @param options Specified options
1021      * @see Base64#ENCODE
1022      * @see Base64#DECODE
1023      * @see Base64#DONT_BREAK_LINES
1024      * @since 2.0
1025      */
1026     public Base64InputStream(InputStream in, int options) {
1027       super(in);
1028       this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1029       this.encode = (options & ENCODE) == ENCODE;
1030       this.bufferLength = encode ? 4 : 3;
1031       this.buffer = new byte[bufferLength];
1032       this.position = -1;
1033       this.lineLength = 0;
1034       this.options = options; // Record for later, mostly to determine which
1035                               // alphabet to use
1036       this.decodabet = getDecodabet(options);
1037     } // end constructor
1038 
1039     /**
1040      * Reads enough of the input stream to convert to/from Base64 and returns
1041      * the next byte.
1042      *
1043      * @return next byte
1044      * @since 1.3
1045      */
1046     @Override
1047     public int read() throws IOException {
1048       // Do we need to get data?
1049       if (position < 0) {
1050         if (encode) {
1051           byte[] b3 = new byte[3];
1052           int numBinaryBytes = 0;
1053           for (int i = 0; i < 3; i++) {
1054             try {
1055               int b = in.read();
1056 
1057               // If end of stream, b is -1.
1058               if (b >= 0) {
1059                 b3[i] = (byte) b;
1060                 numBinaryBytes++;
1061               } // end if: not end of stream
1062 
1063             } catch (IOException e) {
1064               // Only a problem if we got no data at all.
1065               if (i == 0)
1066                 throw e;
1067 
1068             } // end catch
1069           } // end for: each needed input byte
1070 
1071           if (numBinaryBytes > 0) {
1072             encode3to4(b3, 0, numBinaryBytes, buffer, 0, options);
1073             position = 0;
1074             numSigBytes = 4;
1075 
1076           } else {
1077             return -1;
1078           } // end else
1079 
1080         } else {
1081           byte[] b4 = new byte[4];
1082           int i;
1083           for (i = 0; i < 4; i++) {
1084             // Read four "meaningful" bytes:
1085             int b;
1086             do {
1087               b = in.read();
1088             } while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC);
1089 
1090             if (b < 0) {
1091               break; // Reads a -1 if end of stream
1092             }
1093 
1094             b4[i] = (byte) b;
1095           } // end for: each needed input byte
1096 
1097           if (i == 4) {
1098             numSigBytes = decode4to3(b4, 0, buffer, 0, options);
1099             position = 0;
1100 
1101           } else if (i == 0) {
1102             return -1;
1103 
1104           } else {
1105             // Must have broken out from above.
1106             throw new IOException("Improperly padded Base64 input.");
1107           } // end
1108         } // end else: decode
1109       } // end else: get data
1110 
1111       // Got data?
1112       if (position >= 0) {
1113         // End of relevant data?
1114         if ( /* !encode && */position >= numSigBytes) {
1115           return -1;
1116         }
1117 
1118         if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
1119           lineLength = 0;
1120           return '\n';
1121 
1122         }
1123         lineLength++;                   // This isn't important when decoding
1124                                         // but throwing an extra "if" seems
1125                                         // just as wasteful.
1126 
1127         int b = buffer[position++];
1128 
1129         if (position >= bufferLength)
1130           position = -1;
1131 
1132         return b & 0xFF;                // This is how you "cast" a byte that's
1133                                         // intended to be unsigned.
1134 
1135       }
1136 
1137       // When JDK1.4 is more accepted, use an assertion here.
1138       throw new IOException("Error in Base64 code reading stream.");
1139 
1140     } // end read
1141 
1142     /**
1143      * Calls {@link #read()} repeatedly until the end of stream is reached or
1144      * <var>len</var> bytes are read. Returns number of bytes read into array
1145      * or -1 if end of stream is encountered.
1146      *
1147      * @param dest array to hold values
1148      * @param off offset for array
1149      * @param len max number of bytes to read into array
1150      * @return bytes read into array or -1 if end of stream is encountered.
1151      * @since 1.3
1152      */
1153     @Override
1154     public int read(byte[] dest, int off, int len) throws IOException {
1155       int i;
1156       int b;
1157       for (i = 0; i < len; i++) {
1158         b = read();
1159         if (b >= 0) {
1160           dest[off + i] = (byte) b;
1161         } else if (i == 0) {
1162           return -1;
1163         } else {
1164           break; // Out of 'for' loop
1165         }
1166       } // end for: each byte read
1167       return i;
1168     } // end read
1169 
1170   } // end inner class InputStream
1171 
1172   /* ******** I N N E R   C L A S S   O U T P U T S T R E A M ******** */
1173 
1174   /**
1175    * A {@link Base64.Base64OutputStream} will write data to another
1176    * <tt>OutputStream</tt>, given in the constructor, and
1177    * encode/decode to/from Base64 notation on the fly.
1178    *
1179    * @see Base64
1180    * @since 1.3
1181    */
1182   @InterfaceAudience.Private
1183   @InterfaceStability.Stable
1184   public static class Base64OutputStream extends FilterOutputStream {
1185     private boolean encode;
1186     private int position;
1187     private byte[] buffer;
1188     private int bufferLength;
1189     private int lineLength;
1190     private boolean breakLines;
1191     private byte[] b4;                          // Scratch used in a few places
1192     private boolean suspendEncoding;
1193     private int options;                        // Record for later
1194     private byte[] decodabet;                   // Local copy avoids method calls
1195 
1196     /**
1197      * Constructs a {@link Base64OutputStream} in either ENCODE or DECODE mode.
1198      * <p>
1199      * Valid options:
1200      *
1201      * <ul>
1202      *   <li>ENCODE or DECODE: Encode or Decode as data is read.</li>
1203      *   <li>DONT_BREAK_LINES: don't break lines at 76 characters (only
1204      *     meaningful when encoding) <i>Note: Technically, this makes your
1205      *     encoding non-compliant.</i></li>
1206      * </ul>
1207      *
1208      * <p>
1209      * Example: <code>new Base64.Base64OutputStream( out, Base64.ENCODE )</code>
1210      *
1211      * @param out the <tt>OutputStream</tt> to which data will be written.
1212      * @param options Specified options.
1213      * @see Base64#ENCODE
1214      * @see Base64#DECODE
1215      * @see Base64#DONT_BREAK_LINES
1216      * @since 1.3
1217      */
1218     @InterfaceAudience.Private
1219     @InterfaceStability.Stable
1220     public Base64OutputStream(OutputStream out, int options) {
1221       super(out);
1222       this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES;
1223       this.encode = (options & ENCODE) == ENCODE;
1224       this.bufferLength = encode ? 3 : 4;
1225       this.buffer = new byte[bufferLength];
1226       this.position = 0;
1227       this.lineLength = 0;
1228       this.suspendEncoding = false;
1229       this.b4 = new byte[4];
1230       this.options = options;
1231       this.decodabet = getDecodabet(options);
1232     } // end constructor
1233 
1234     /**
1235      * Writes the byte to the output stream after converting to/from Base64
1236      * notation. When encoding, bytes are buffered three at a time before the
1237      * output stream actually gets a write() call. When decoding, bytes are
1238      * buffered four at a time.
1239      *
1240      * @param theByte the byte to write
1241      * @since 1.3
1242      */
1243     @Override
1244     public void write(int theByte) throws IOException {
1245       // Encoding suspended?
1246       if (suspendEncoding) {
1247         super.out.write(theByte);
1248         return;
1249       } // end if: supsended
1250 
1251       // Encode?
1252       if (encode) {
1253         buffer[position++] = (byte) theByte;
1254         if (position >= bufferLength) {                 // Enough to encode.
1255           out.write(encode3to4(b4, buffer, bufferLength, options));
1256           lineLength += 4;
1257           if (breakLines && lineLength >= MAX_LINE_LENGTH) {
1258             out.write(NEW_LINE);
1259             lineLength = 0;
1260           } // end if: end of line
1261 
1262           position = 0;
1263         } // end if: enough to output
1264 
1265       } else {
1266         // Meaningful Base64 character?
1267         if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) {
1268           buffer[position++] = (byte) theByte;
1269           if (position >= bufferLength) {               // Enough to output.
1270             int len = decode4to3(buffer, 0, b4, 0, options);
1271             out.write(b4, 0, len);
1272             position = 0;
1273           } // end if: enough to output
1274 
1275         } else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
1276           throw new IOException("Invalid character in Base64 data.");
1277         } // end else: not white space either
1278       } // end else: decoding
1279     } // end write
1280 
1281     /**
1282      * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are
1283      * written.
1284      *
1285      * @param theBytes array from which to read bytes
1286      * @param off offset for array
1287      * @param len max number of bytes to read into array
1288      * @since 1.3
1289      */
1290     @Override
1291     public void write(byte[] theBytes, int off, int len) throws IOException {
1292       // Encoding suspended?
1293       if (suspendEncoding) {
1294         super.out.write(theBytes, off, len);
1295         return;
1296       } // end if: supsended
1297 
1298       for (int i = 0; i < len; i++) {
1299         write(theBytes[off + i]);
1300       } // end for: each byte written
1301 
1302     } // end write
1303 
1304     /**
1305      * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer without
1306      * closing the stream.
1307      *
1308      * @throws IOException e
1309      */
1310     public void flushBase64() throws IOException {
1311       if (position > 0) {
1312         if (encode) {
1313           out.write(encode3to4(b4, buffer, position, options));
1314           position = 0;
1315 
1316         } else {
1317           throw new IOException("Base64 input not properly padded.");
1318         } // end else: decoding
1319       } // end if: buffer partially full
1320 
1321     } // end flush
1322 
1323     /**
1324      * Flushes and closes (I think, in the superclass) the stream.
1325      *
1326      * @since 1.3
1327      */
1328     @Override
1329     public void close() throws IOException {
1330       // 1. Ensure that pending characters are written
1331       flushBase64();
1332 
1333       // 2. Actually close the stream
1334       // Base class both flushes and closes.
1335       super.close();
1336 
1337       buffer = null;
1338       out = null;
1339     } // end close
1340 
1341   } // end inner class OutputStream
1342 
1343 } // end class Base64