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