1 /*******************************************************************************
2 * This program and the accompanying materials are made available under the
3 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
4 * and is available at http://www.eclipse.org/legal/epl-v10.html
7 * Robert Harder <rob@iharder.net> - API and implementation
8 * VTT Technical Research Centre of Finland - tagged with EPL license
9 *******************************************************************************/
10 package org.simantics.utils.bytes;
13 * <p>Encodes and decodes to and from Base64 notation.</p>
14 * <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p>
18 * <code>String encoded = Base64.encode( myByteArray );</code>
20 * <code>byte[] myByteArray = Base64.decode( encoded );</code>
22 * <p>The <tt>options</tt> parameter, which appears in a few places, is used to pass
23 * several pieces of information to the encoder. In the "higher level" methods such as
24 * encodeBytes( bytes, options ) the options parameter can be used to indicate such
25 * things as first gzipping the bytes before encoding them, not inserting linefeeds,
26 * and encoding using the URL-safe and Ordered dialects.</p>
28 * <p>Note, according to <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>,
29 * Section 2.1, implementations should not add line feeds unless explicitly told
30 * to do so. I've got Base64 set to this behavior now, although earlier versions
31 * broke lines by default.</p>
33 * <p>The constants defined in Base64 can be OR-ed together to combine options, so you
34 * might make a call like this:</p>
36 * <code>String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES );</code>
37 * <p>to compress the data before encoding it and then making the output have newline characters.</p>
39 * <code>String encoded = Base64.encodeBytes( crazyString.getBytes() );</code>
47 * <li>v2.3.6 - Fixed bug when breaking lines and the final byte of the encoded
48 * string ended in the last column; the buffer was not properly shrunk and
49 * contained an extra (null) byte that made it into the string.</li>
50 * <li>v2.3.5 - Fixed bug in {@link #encodeFromFile} where estimated buffer size
51 * was wrong for files of size 31, 34, and 37 bytes.</li>
52 * <li>v2.3.4 - Fixed bug when working with gzipped streams whereby flushing
53 * the Base64.OutputStream closed the Base64 encoding (by padding with equals
54 * signs) too soon. Also added an option to suppress the automatic decoding
55 * of gzipped streams. Also added experimental support for specifying a
56 * class loader when using the
57 * {@link #decodeToObject(java.lang.String, int, java.lang.ClassLoader)}
59 * <li>v2.3.3 - Changed default char encoding to US-ASCII which reduces the internal Java
60 * footprint with its CharEncoders and so forth. Fixed some javadocs that were
61 * inconsistent. Removed imports and specified things like java.io.IOException
62 * explicitly inline.</li>
63 * <li>v2.3.2 - Reduced memory footprint! Finally refined the "guessing" of how big the
64 * final encoded data will be so that the code doesn't have to create two output
65 * arrays: an oversized initial one and then a final, exact-sized one. Big win
66 * when using the {@link #encodeBytesToBytes(byte[])} family of methods (and not
67 * using the gzip options which uses a different mechanism with streams and stuff).</li>
68 * <li>v2.3.1 - Added {@link #encodeBytesToBytes(byte[], int, int, int)} and some
69 * similar helper methods to be more efficient with memory by not returning a
70 * String but just a byte array.</li>
71 * <li>v2.3 - <strong>This is not a drop-in replacement!</strong> This is two years of comments
72 * and bug fixes queued up and finally executed. Thanks to everyone who sent
73 * me stuff, and I'm sorry I wasn't able to distribute your fixes to everyone else.
74 * Much bad coding was cleaned up including throwing exceptions where necessary
75 * instead of returning null values or something similar. Here are some changes
76 * that may affect you:
78 * <li><em>Does not break lines, by default.</em> This is to keep in compliance with
79 * <a href="http://www.faqs.org/rfcs/rfc3548.html">RFC3548</a>.</li>
80 * <li><em>Throws exceptions instead of returning null values.</em> Because some operations
81 * (especially those that may permit the GZIP option) use IO streams, there
82 * is a possiblity of an java.io.IOException being thrown. After some discussion and
83 * thought, I've changed the behavior of the methods to throw java.io.IOExceptions
84 * rather than return null if ever there's an error. I think this is more
85 * appropriate, though it will require some changes to your code. Sorry,
86 * it should have been done this way to begin with.</li>
87 * <li><em>Removed all references to System.out, System.err, and the like.</em>
88 * Shame on me. All I can say is sorry they were ever there.</li>
89 * <li><em>Throws NullPointerExceptions and IllegalArgumentExceptions</em> as needed
90 * such as when passed arrays are null or offsets are invalid.</li>
91 * <li>Cleaned up as much javadoc as I could to avoid any javadoc warnings.
92 * This was especially annoying before for people who were thorough in their
93 * own projects and then had gobs of javadoc warnings on this file.</li>
95 * <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug
96 * when using very small files (~< 40 bytes).</li>
97 * <li>v2.2 - Added some helper methods for encoding/decoding directly from
98 * one file to the next. Also added a main() method to support command line
99 * encoding/decoding from one file to the next. Also added these Base64 dialects:
101 * <li>The default is RFC3548 format.</li>
102 * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates
103 * URL and file name friendly format as described in Section 4 of RFC3548.
104 * http://www.faqs.org/rfcs/rfc3548.html</li>
105 * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates
106 * URL and file name friendly format that preserves lexical ordering as described
107 * in http://www.faqs.org/qa/rfcc-1940.html</li>
109 * Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">http://www.powerset.com/</a>
110 * for contributing the new Base64 dialects.
113 * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
114 * some convenience methods for reading and writing to and from files.</li>
115 * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
116 * with other encodings (like EBCDIC).</li>
117 * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
118 * encoded data was a single byte.</li>
119 * <li>v2.0 - I got rid of methods that used booleans to set options.
120 * Now everything is more consolidated and cleaner. The code now detects
121 * when data that's being decoded is gzip-compressed and will decompress it
122 * automatically. Generally things are cleaner. You'll probably have to
123 * change some method calls that you were making to support the new
124 * options format (<tt>int</tt>s that you "OR" together).</li>
125 * <li>v1.5.1 - Fixed bug when decompressing and decoding to a
126 * byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.
127 * Added the ability to "suspend" encoding in the Output Stream so
128 * you can turn on and off the encoding if you need to embed base64
129 * data in an otherwise "normal" stream (like an XML file).</li>
130 * <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
131 * This helps when using GZIP streams.
132 * Added the ability to GZip-compress objects before encoding them.</li>
133 * <li>v1.4 - Added helper methods to read/write files.</li>
134 * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
135 * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
136 * where last buffer being read, if not completely full, was not returned.</li>
137 * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
138 * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
142 * I am placing this code in the Public Domain. Do with it as you will.
143 * This software comes with no guarantees or warranties but with
144 * plenty of well-wishing instead!
145 * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
146 * periodically to check for updates or to contribute improvements.
149 * @author Robert Harder
150 * @author rob@iharder.net
156 /* ******** P U B L I C F I E L D S ******** */
159 /** No options specified. Value is zero. */
160 public final static int NO_OPTIONS = 0;
162 /** Specify encoding in first bit. Value is one. */
163 public final static int ENCODE = 1;
166 /** Specify decoding in first bit. Value is zero. */
167 public final static int DECODE = 0;
170 /** Specify that data should be gzip-compressed in second bit. Value is two. */
171 public final static int GZIP = 2;
173 /** Specify that gzipped data should <em>not</em> be automatically gunzipped. */
174 public final static int DONT_GUNZIP = 4;
177 /** Do break lines when encoding. Value is 8. */
178 public final static int DO_BREAK_LINES = 8;
181 * Encode using Base64-like encoding that is URL- and Filename-safe as described
182 * in Section 4 of RFC3548:
183 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
184 * It is important to note that data encoded this way is <em>not</em> officially valid Base64,
185 * or at the very least should not be called Base64 without also specifying that is
186 * was encoded using the URL- and Filename-safe dialect.
188 public final static int URL_SAFE = 16;
192 * Encode using the special "ordered" dialect of Base64 described here:
193 * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
195 public final static int ORDERED = 32;
198 /* ******** P R I V A T E F I E L D S ******** */
201 /** Maximum line length (76) of Base64 output. */
202 private final static int MAX_LINE_LENGTH = 76;
205 /** The equals sign (=) as a byte. */
206 private final static byte EQUALS_SIGN = (byte)'=';
209 /** The new line character (\n) as a byte. */
210 private final static byte NEW_LINE = (byte)'\n';
213 /** Preferred encoding. */
214 private final static String PREFERRED_ENCODING = "US-ASCII";
217 private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
218 private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
221 /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
223 /** The 64 valid Base64 values. */
224 /* Host platform me be something funny like EBCDIC, so we hardcode these values. */
225 private final static byte[] _STANDARD_ALPHABET = {
226 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
227 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
228 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
229 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
230 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
231 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
232 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
233 (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
234 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
235 (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
240 * Translates a Base64 value to either its 6-bit reconstruction value
241 * or a negative number indicating some other meaning.
243 private final static byte[] _STANDARD_DECODABET = {
244 -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
245 -5,-5, // Whitespace: Tab and Linefeed
246 -9,-9, // Decimal 11 - 12
247 -5, // Whitespace: Carriage Return
248 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
249 -9,-9,-9,-9,-9, // Decimal 27 - 31
250 -5, // Whitespace: Space
251 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
252 62, // Plus sign at decimal 43
253 -9,-9,-9, // Decimal 44 - 46
254 63, // Slash at decimal 47
255 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
256 -9,-9,-9, // Decimal 58 - 60
257 -1, // Equals sign at decimal 61
258 -9,-9,-9, // Decimal 62 - 64
259 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
260 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
261 -9,-9,-9,-9,-9,-9, // Decimal 91 - 96
262 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
263 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
264 -9,-9,-9,-9 // Decimal 123 - 126
265 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
266 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
267 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
268 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
269 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
270 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
271 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
272 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
273 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
274 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
278 /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
281 * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548:
282 * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>.
283 * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash."
285 private final static byte[] _URL_SAFE_ALPHABET = {
286 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
287 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
288 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
289 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
290 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
291 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
292 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
293 (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
294 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
295 (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_'
299 * Used in decoding URL- and Filename-safe dialects of Base64.
301 private final static byte[] _URL_SAFE_DECODABET = {
302 -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
303 -5,-5, // Whitespace: Tab and Linefeed
304 -9,-9, // Decimal 11 - 12
305 -5, // Whitespace: Carriage Return
306 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
307 -9,-9,-9,-9,-9, // Decimal 27 - 31
308 -5, // Whitespace: Space
309 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
310 -9, // Plus sign at decimal 43
312 62, // Minus sign at decimal 45
314 -9, // Slash at decimal 47
315 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
316 -9,-9,-9, // Decimal 58 - 60
317 -1, // Equals sign at decimal 61
318 -9,-9,-9, // Decimal 62 - 64
319 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
320 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
321 -9,-9,-9,-9, // Decimal 91 - 94
322 63, // Underscore at decimal 95
324 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
325 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
326 -9,-9,-9,-9 // Decimal 123 - 126
327 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
328 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
329 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
330 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
331 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
332 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
333 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
334 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
335 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
336 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
341 /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
344 * I don't get the point of this technique, but someone requested it,
345 * and it is described here:
346 * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
348 private final static byte[] _ORDERED_ALPHABET = {
350 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4',
351 (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9',
352 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
353 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
354 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
355 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
357 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
358 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
359 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
360 (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z'
364 * Used in decoding the "ordered" dialect of Base64.
366 private final static byte[] _ORDERED_DECODABET = {
367 -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
368 -5,-5, // Whitespace: Tab and Linefeed
369 -9,-9, // Decimal 11 - 12
370 -5, // Whitespace: Carriage Return
371 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
372 -9,-9,-9,-9,-9, // Decimal 27 - 31
373 -5, // Whitespace: Space
374 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
375 -9, // Plus sign at decimal 43
377 0, // Minus sign at decimal 45
379 -9, // Slash at decimal 47
380 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine
381 -9,-9,-9, // Decimal 58 - 60
382 -1, // Equals sign at decimal 61
383 -9,-9,-9, // Decimal 62 - 64
384 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M'
385 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z'
386 -9,-9,-9,-9, // Decimal 91 - 94
387 37, // Underscore at decimal 95
389 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm'
390 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z'
391 -9,-9,-9,-9 // Decimal 123 - 126
392 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
393 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
394 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
395 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
396 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
397 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
398 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
399 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
400 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
401 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
405 /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */
409 * Returns one of the _SOMETHING_ALPHABET byte arrays depending on
410 * the options specified.
411 * It's possible, though silly, to specify ORDERED <b>and</b> URLSAFE
412 * in which case one of them will be picked, though there is
413 * no guarantee as to which one will be picked.
415 private final static byte[] getAlphabet( int options ) {
416 if ((options & URL_SAFE) == URL_SAFE) {
417 return _URL_SAFE_ALPHABET;
418 } else if ((options & ORDERED) == ORDERED) {
419 return _ORDERED_ALPHABET;
421 return _STANDARD_ALPHABET;
427 * Returns one of the _SOMETHING_DECODABET byte arrays depending on
428 * the options specified.
429 * It's possible, though silly, to specify ORDERED and URL_SAFE
430 * in which case one of them will be picked, though there is
431 * no guarantee as to which one will be picked.
433 private final static byte[] getDecodabet( int options ) {
434 if( (options & URL_SAFE) == URL_SAFE) {
435 return _URL_SAFE_DECODABET;
436 } else if ((options & ORDERED) == ORDERED) {
437 return _ORDERED_DECODABET;
439 return _STANDARD_DECODABET;
445 /** Defeats instantiation. */
451 /* ******** E N C O D I N G M E T H O D S ******** */
455 * Encodes up to the first three bytes of array <var>threeBytes</var>
456 * and returns a four-byte array in Base64 notation.
457 * The actual number of significant bytes in your array is
458 * given by <var>numSigBytes</var>.
459 * The array <var>threeBytes</var> needs only be as big as
460 * <var>numSigBytes</var>.
461 * Code can reuse a byte array by passing a four-byte array as <var>b4</var>.
463 * @param b4 A reusable byte array to reduce array instantiation
464 * @param threeBytes the array to convert
465 * @param numSigBytes the number of significant bytes in your array
466 * @return four byte array in Base64 notation.
469 private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) {
470 encode3to4( threeBytes, 0, numSigBytes, b4, 0, options );
476 * <p>Encodes up to three bytes of the array <var>source</var>
477 * and writes the resulting four Base64 bytes to <var>destination</var>.
478 * The source and destination arrays can be manipulated
479 * anywhere along their length by specifying
480 * <var>srcOffset</var> and <var>destOffset</var>.
481 * This method does not check to make sure your arrays
482 * are large enough to accomodate <var>srcOffset</var> + 3 for
483 * the <var>source</var> array or <var>destOffset</var> + 4 for
484 * the <var>destination</var> array.
485 * The actual number of significant bytes in your array is
486 * given by <var>numSigBytes</var>.</p>
487 * <p>This is the lowest level of the encoding methods with
488 * all possible parameters.</p>
490 * @param source the array to convert
491 * @param srcOffset the index where conversion begins
492 * @param numSigBytes the number of significant bytes in your array
493 * @param destination the array to hold the conversion
494 * @param destOffset the index where output will be put
495 * @return the <var>destination</var> array
498 private static byte[] encode3to4(
499 byte[] source, int srcOffset, int numSigBytes,
500 byte[] destination, int destOffset, int options ) {
502 byte[] ALPHABET = getAlphabet( options );
505 // 01234567890123456789012345678901 Bit position
506 // --------000000001111111122222222 Array position from threeBytes
507 // --------| || || || | Six bit groups to index ALPHABET
508 // >>18 >>12 >> 6 >> 0 Right shift necessary
509 // 0x3f 0x3f 0x3f Additional AND
511 // Create buffer with zero-padding if there are only one or two
512 // significant bytes passed in the array.
513 // We have to shift left 24 in order to flush out the 1's that appear
514 // when Java treats a value as negative that is cast from a byte to an int.
515 int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
516 | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
517 | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
519 switch( numSigBytes )
522 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
523 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
524 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
525 destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
529 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
530 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
531 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
532 destination[ destOffset + 3 ] = EQUALS_SIGN;
536 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
537 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
538 destination[ destOffset + 2 ] = EQUALS_SIGN;
539 destination[ destOffset + 3 ] = EQUALS_SIGN;
550 * Performs Base64 encoding on the <code>raw</code> ByteBuffer,
551 * writing it to the <code>encoded</code> ByteBuffer.
552 * This is an experimental feature. Currently it does not
553 * pass along any options (such as {@link #DO_BREAK_LINES}
556 * @param raw input buffer
557 * @param encoded output buffer
560 public static void encode( java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded ){
561 byte[] raw3 = new byte[3];
562 byte[] enc4 = new byte[4];
564 while( raw.hasRemaining() ){
565 int rem = Math.min(3,raw.remaining());
567 Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
569 } // end input remaining
574 * Performs Base64 encoding on the <code>raw</code> ByteBuffer,
575 * writing it to the <code>encoded</code> CharBuffer.
576 * This is an experimental feature. Currently it does not
577 * pass along any options (such as {@link #DO_BREAK_LINES}
580 * @param raw input buffer
581 * @param encoded output buffer
584 public static void encode( java.nio.ByteBuffer raw, java.nio.CharBuffer encoded ){
585 byte[] raw3 = new byte[3];
586 byte[] enc4 = new byte[4];
588 while( raw.hasRemaining() ){
589 int rem = Math.min(3,raw.remaining());
591 Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
592 for( int i = 0; i < 4; i++ ){
593 encoded.put( (char)(enc4[i] & 0xFF) );
595 } // end input remaining
602 * Serializes an object and returns the Base64-encoded
603 * version of that serialized object.
605 * <p>As of v 2.3, if the object
606 * cannot be serialized or there is another error,
607 * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
608 * In earlier versions, it just returned a null value, but
609 * in retrospect that's a pretty poor way to handle it.</p>
611 * The object is not GZip-compressed before being encoded.
613 * @param serializableObject The object to encode
614 * @return The Base64-encoded object
615 * @throws java.io.IOException if there is an error
616 * @throws NullPointerException if serializedObject is null
619 public static String encodeObject( java.io.Serializable serializableObject )
620 throws java.io.IOException {
621 return encodeObject( serializableObject, NO_OPTIONS );
622 } // end encodeObject
627 * Serializes an object and returns the Base64-encoded
628 * version of that serialized object.
630 * <p>As of v 2.3, if the object
631 * cannot be serialized or there is another error,
632 * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
633 * In earlier versions, it just returned a null value, but
634 * in retrospect that's a pretty poor way to handle it.</p>
636 * The object is not GZip-compressed before being encoded.
638 * Example options:<pre>
639 * GZIP: gzip-compresses object before encoding it.
640 * DO_BREAK_LINES: break lines at 76 characters
643 * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
645 * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
647 * @param serializableObject The object to encode
648 * @param options Specified options
649 * @return The Base64-encoded object
651 * @see Base64#DO_BREAK_LINES
652 * @throws java.io.IOException if there is an error
655 public static String encodeObject( java.io.Serializable serializableObject, int options )
656 throws java.io.IOException {
658 if( serializableObject == null ){
659 throw new NullPointerException( "Cannot serialize a null object." );
663 java.io.ByteArrayOutputStream baos = null;
664 java.io.OutputStream b64os = null;
665 java.util.zip.GZIPOutputStream gzos = null;
666 java.io.ObjectOutputStream oos = null;
670 // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
671 baos = new java.io.ByteArrayOutputStream();
672 b64os = new Base64.OutputStream( baos, ENCODE | options );
673 if( (options & GZIP) != 0 ){
675 gzos = new java.util.zip.GZIPOutputStream(b64os);
676 oos = new java.io.ObjectOutputStream( gzos );
679 oos = new java.io.ObjectOutputStream( b64os );
681 oos.writeObject( serializableObject );
683 catch( java.io.IOException e ) {
684 // Catch it and then throw it immediately so that
685 // the finally{} block is called for cleanup.
689 try{ oos.close(); } catch( Exception e ){}
690 try{ gzos.close(); } catch( Exception e ){}
691 try{ b64os.close(); } catch( Exception e ){}
692 try{ baos.close(); } catch( Exception e ){}
695 // Return value according to relevant encoding.
697 return new String( baos.toByteArray(), PREFERRED_ENCODING );
699 catch (java.io.UnsupportedEncodingException uue){
700 // Fall back to some Java default
701 return new String( baos.toByteArray() );
708 * Encodes a byte array into Base64 notation.
709 * Does not GZip-compress data.
711 * Added by Tuukka Lehtonen for backwards binary compatibility
712 * with previous Base64 Simantics implementation.
714 * @param source The data to convert
715 * @return The data in Base64-encoded form
716 * @throws NullPointerException if source array is null
718 public static String encode( byte[] source ) {
719 return encodeBytes( source );
723 * Encodes a byte array into Base64 notation.
724 * Does not GZip-compress data.
726 * @param source The data to convert
727 * @return The data in Base64-encoded form
728 * @throws NullPointerException if source array is null
731 public static String encodeBytes( byte[] source ) {
732 // Since we're not going to have the GZIP encoding turned on,
733 // we're not going to have an java.io.IOException thrown, so
734 // we should not force the user to have to catch it.
735 String encoded = null;
737 encoded = encodeBytes(source, 0, source.length, NO_OPTIONS);
738 } catch (java.io.IOException ex) {
739 assert false : ex.getMessage();
741 assert encoded != null;
748 * Encodes a byte array into Base64 notation.
750 * Example options:<pre>
751 * GZIP: gzip-compresses object before encoding it.
752 * DO_BREAK_LINES: break lines at 76 characters
753 * <i>Note: Technically, this makes your encoding non-compliant.</i>
756 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
758 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
761 * <p>As of v 2.3, if there is an error with the GZIP stream,
762 * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
763 * In earlier versions, it just returned a null value, but
764 * in retrospect that's a pretty poor way to handle it.</p>
767 * @param source The data to convert
768 * @param options Specified options
769 * @return The Base64-encoded data as a String
771 * @see Base64#DO_BREAK_LINES
772 * @throws java.io.IOException if there is an error
773 * @throws NullPointerException if source array is null
776 public static String encodeBytes( byte[] source, int options ) throws java.io.IOException {
777 return encodeBytes( source, 0, source.length, options );
782 * Encodes a byte array into Base64 notation.
783 * Does not GZip-compress data.
785 * <p>As of v 2.3, if there is an error,
786 * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
787 * In earlier versions, it just returned a null value, but
788 * in retrospect that's a pretty poor way to handle it.</p>
791 * @param source The data to convert
792 * @param off Offset in array where conversion should begin
793 * @param len Length of data to convert
794 * @return The Base64-encoded data as a String
795 * @throws NullPointerException if source array is null
796 * @throws IllegalArgumentException if source array, offset, or length are invalid
799 public static String encodeBytes( byte[] source, int off, int len ) {
800 // Since we're not going to have the GZIP encoding turned on,
801 // we're not going to have an java.io.IOException thrown, so
802 // we should not force the user to have to catch it.
803 String encoded = null;
805 encoded = encodeBytes( source, off, len, NO_OPTIONS );
806 } catch (java.io.IOException ex) {
807 assert false : ex.getMessage();
809 assert encoded != null;
816 * Encodes a byte array into Base64 notation.
818 * Example options:<pre>
819 * GZIP: gzip-compresses object before encoding it.
820 * DO_BREAK_LINES: break lines at 76 characters
821 * <i>Note: Technically, this makes your encoding non-compliant.</i>
824 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
826 * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
829 * <p>As of v 2.3, if there is an error with the GZIP stream,
830 * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
831 * In earlier versions, it just returned a null value, but
832 * in retrospect that's a pretty poor way to handle it.</p>
835 * @param source The data to convert
836 * @param off Offset in array where conversion should begin
837 * @param len Length of data to convert
838 * @param options Specified options
839 * @return The Base64-encoded data as a String
841 * @see Base64#DO_BREAK_LINES
842 * @throws java.io.IOException if there is an error
843 * @throws NullPointerException if source array is null
844 * @throws IllegalArgumentException if source array, offset, or length are invalid
847 public static String encodeBytes( byte[] source, int off, int len, int options ) throws java.io.IOException {
848 byte[] encoded = encodeBytesToBytes( source, off, len, options );
850 // Return value according to relevant encoding.
852 return new String( encoded, PREFERRED_ENCODING );
854 catch (java.io.UnsupportedEncodingException uue) {
855 return new String( encoded );
864 * Similar to {@link #encodeBytes(byte[])} but returns
865 * a byte array instead of instantiating a String. This is more efficient
866 * if you're working with I/O streams and have large data sets to encode.
869 * @param source The data to convert
870 * @return The Base64-encoded data as a byte[] (of ASCII characters)
871 * @throws NullPointerException if source array is null
874 public static byte[] encodeBytesToBytes( byte[] source ) {
875 byte[] encoded = null;
877 encoded = encodeBytesToBytes( source, 0, source.length, Base64.NO_OPTIONS );
878 } catch( java.io.IOException ex ) {
879 assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage();
886 * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns
887 * a byte array instead of instantiating a String. This is more efficient
888 * if you're working with I/O streams and have large data sets to encode.
891 * @param source The data to convert
892 * @param off Offset in array where conversion should begin
893 * @param len Length of data to convert
894 * @param options Specified options
895 * @return The Base64-encoded data as a String
897 * @see Base64#DO_BREAK_LINES
898 * @throws java.io.IOException if there is an error
899 * @throws NullPointerException if source array is null
900 * @throws IllegalArgumentException if source array, offset, or length are invalid
903 public static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) throws java.io.IOException {
905 if( source == null ){
906 throw new NullPointerException( "Cannot serialize a null array." );
910 throw new IllegalArgumentException( "Cannot have negative offset: " + off );
914 throw new IllegalArgumentException( "Cannot have length offset: " + len );
917 if( off + len > source.length ){
918 throw new IllegalArgumentException(
919 String.format( "Cannot have offset of %d and length of %d with array of length %d", off,len,source.length));
925 if( (options & GZIP) != 0 ) {
926 java.io.ByteArrayOutputStream baos = null;
927 java.util.zip.GZIPOutputStream gzos = null;
928 Base64.OutputStream b64os = null;
931 // GZip -> Base64 -> ByteArray
932 baos = new java.io.ByteArrayOutputStream();
933 b64os = new Base64.OutputStream( baos, ENCODE | options );
934 gzos = new java.util.zip.GZIPOutputStream( b64os );
936 gzos.write( source, off, len );
939 catch( java.io.IOException e ) {
940 // Catch it and then throw it immediately so that
941 // the finally{} block is called for cleanup.
945 try{ gzos.close(); } catch( Exception e ){}
946 try{ b64os.close(); } catch( Exception e ){}
947 try{ baos.close(); } catch( Exception e ){}
950 return baos.toByteArray();
951 } // end if: compress
953 // Else, don't compress. Better not to use streams at all then.
955 boolean breakLines = (options & DO_BREAK_LINES) != 0;
957 //int len43 = len * 4 / 3;
958 //byte[] outBuff = new byte[ ( len43 ) // Main 4:3
959 // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
960 // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
961 // Try to determine more precisely how big the array needs to be.
962 // If we get it right, we don't have to do an array copy, and
963 // we save a bunch of memory.
964 int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding
966 encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters
968 byte[] outBuff = new byte[ encLen ];
975 for( ; d < len2; d+=3, e+=4 ) {
976 encode3to4( source, d+off, 3, outBuff, e, options );
979 if( breakLines && lineLength >= MAX_LINE_LENGTH )
981 outBuff[e+4] = NEW_LINE;
984 } // end if: end of line
985 } // en dfor: each piece of array
988 encode3to4( source, d+off, len - d, outBuff, e, options );
990 } // end if: some padding needed
993 // Only resize array if we didn't guess it right.
994 if( e <= outBuff.length - 1 ){
995 // If breaking lines and the last byte falls right at
996 // the line length (76 bytes per line), there will be
997 // one extra byte, and the array will need to be resized.
998 // Not too bad of an estimate on array size, I'd say.
999 byte[] finalOut = new byte[e];
1000 System.arraycopy(outBuff,0, finalOut,0,e);
1001 //System.err.println("Having to resize array from " + outBuff.length + " to " + e );
1004 //System.err.println("No need to resize array.");
1008 } // end else: don't compress
1010 } // end encodeBytesToBytes
1016 /* ******** D E C O D I N G M E T H O D S ******** */
1020 * Decodes four bytes from array <var>source</var>
1021 * and writes the resulting bytes (up to three of them)
1022 * to <var>destination</var>.
1023 * The source and destination arrays can be manipulated
1024 * anywhere along their length by specifying
1025 * <var>srcOffset</var> and <var>destOffset</var>.
1026 * This method does not check to make sure your arrays
1027 * are large enough to accomodate <var>srcOffset</var> + 4 for
1028 * the <var>source</var> array or <var>destOffset</var> + 3 for
1029 * the <var>destination</var> array.
1030 * This method returns the actual number of bytes that
1031 * were converted from the Base64 encoding.
1032 * <p>This is the lowest level of the decoding methods with
1033 * all possible parameters.</p>
1036 * @param source the array to convert
1037 * @param srcOffset the index where conversion begins
1038 * @param destination the array to hold the conversion
1039 * @param destOffset the index where output will be put
1040 * @param options alphabet type is pulled from this (standard, url-safe, ordered)
1041 * @return the number of decoded bytes converted
1042 * @throws NullPointerException if source or destination arrays are null
1043 * @throws IllegalArgumentException if srcOffset or destOffset are invalid
1044 * or there is not enough room in the array.
1047 private static int decode4to3(
1048 byte[] source, int srcOffset,
1049 byte[] destination, int destOffset, int options ) {
1051 // Lots of error checking and exception throwing
1052 if( source == null ){
1053 throw new NullPointerException( "Source array was null." );
1055 if( destination == null ){
1056 throw new NullPointerException( "Destination array was null." );
1058 if( srcOffset < 0 || srcOffset + 3 >= source.length ){
1059 throw new IllegalArgumentException( String.format(
1060 "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) );
1062 if( destOffset < 0 || destOffset +2 >= destination.length ){
1063 throw new IllegalArgumentException( String.format(
1064 "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) );
1068 byte[] DECODABET = getDecodabet( options );
1071 if( source[ srcOffset + 2] == EQUALS_SIGN ) {
1072 // Two ways to do the same thing. Don't know which way I like best.
1073 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
1074 // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
1075 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
1076 | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
1078 destination[ destOffset ] = (byte)( outBuff >>> 16 );
1083 else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) {
1084 // Two ways to do the same thing. Don't know which way I like best.
1085 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
1086 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
1087 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
1088 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
1089 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
1090 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 );
1092 destination[ destOffset ] = (byte)( outBuff >>> 16 );
1093 destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 );
1099 // Two ways to do the same thing. Don't know which way I like best.
1100 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
1101 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
1102 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
1103 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
1104 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
1105 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
1106 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6)
1107 | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
1110 destination[ destOffset ] = (byte)( outBuff >> 16 );
1111 destination[ destOffset + 1 ] = (byte)( outBuff >> 8 );
1112 destination[ destOffset + 2 ] = (byte)( outBuff );
1116 } // end decodeToBytes
1123 * Low-level access to decoding ASCII characters in
1124 * the form of a byte array. <strong>Ignores GUNZIP option, if
1125 * it's set.</strong> This is not generally a recommended method,
1126 * although it is used internally as part of the decoding process.
1127 * Special case: if len = 0, an empty array is returned. Still,
1128 * if you need more speed and reduced memory footprint (and aren't
1129 * gzipping), consider this method.
1131 * @param source The Base64 encoded data
1132 * @return decoded data
1135 public static byte[] decode( byte[] source ){
1136 byte[] decoded = null;
1138 decoded = decode( source, 0, source.length, Base64.NO_OPTIONS );
1139 } catch( java.io.IOException ex ) {
1140 assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage();
1148 * Low-level access to decoding ASCII characters in
1149 * the form of a byte array. <strong>Ignores GUNZIP option, if
1150 * it's set.</strong> This is not generally a recommended method,
1151 * although it is used internally as part of the decoding process.
1152 * Special case: if len = 0, an empty array is returned. Still,
1153 * if you need more speed and reduced memory footprint (and aren't
1154 * gzipping), consider this method.
1156 * @param source The Base64 encoded data
1157 * @param off The offset of where to begin decoding
1158 * @param len The length of characters to decode
1159 * @param options Can specify options such as alphabet type to use
1160 * @return decoded data
1161 * @throws java.io.IOException If bogus characters exist in source data
1164 public static byte[] decode( byte[] source, int off, int len, int options )
1165 throws java.io.IOException {
1167 // Lots of error checking and exception throwing
1168 if( source == null ){
1169 throw new NullPointerException( "Cannot decode null source array." );
1171 if( off < 0 || off + len > source.length ){
1172 throw new IllegalArgumentException( String.format(
1173 "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) );
1178 }else if( len < 4 ){
1179 throw new IllegalArgumentException(
1180 "Base64-encoded string must have at least four characters, but length specified was " + len );
1183 byte[] DECODABET = getDecodabet( options );
1185 int len34 = len * 3 / 4; // Estimate on array size
1186 byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
1187 int outBuffPosn = 0; // Keep track of where we're writing
1189 byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space
1190 int b4Posn = 0; // Keep track of four byte input buffer
1191 int i = 0; // Source array counter
1192 byte sbiCrop = 0; // Low seven bits (ASCII) of input
1193 byte sbiDecode = 0; // Special value from DECODABET
1195 for( i = off; i < off+len; i++ ) { // Loop through source
1197 sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
1198 sbiDecode = DECODABET[ sbiCrop ]; // Special value
1200 // White space, Equals sign, or legit Base64 character
1201 // Note the values such as -5 and -9 in the
1202 // DECODABETs at the top of the file.
1203 if( sbiDecode >= WHITE_SPACE_ENC ) {
1204 if( sbiDecode >= EQUALS_SIGN_ENC ) {
1205 b4[ b4Posn++ ] = sbiCrop; // Save non-whitespace
1206 if( b4Posn > 3 ) { // Time to decode?
1207 outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options );
1210 // If that was the equals sign, break out of 'for' loop
1211 if( sbiCrop == EQUALS_SIGN ) {
1213 } // end if: equals sign
1214 } // end if: quartet built
1215 } // end if: equals sign or better
1216 } // end if: white space, equals sign or better
1218 // There's a bad input character in the Base64 stream.
1219 throw new java.io.IOException( String.format(
1220 "Bad Base64 input character '%c' in array position %d", source[i], i ) );
1222 } // each input character
1224 byte[] out = new byte[ outBuffPosn ];
1225 System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
1233 * Decodes data from Base64 notation, automatically
1234 * detecting gzip-compressed data and decompressing it.
1236 * @param s the string to decode
1237 * @return the decoded data
1238 * @throws java.io.IOException If there is a problem
1241 public static byte[] decode( String s ) throws java.io.IOException {
1242 return decode( s, NO_OPTIONS );
1248 * Decodes data from Base64 notation, automatically
1249 * detecting gzip-compressed data and decompressing it.
1251 * @param s the string to decode
1252 * @param options encode options such as URL_SAFE
1253 * @return the decoded data
1254 * @throws java.io.IOException if there is an error
1255 * @throws NullPointerException if <tt>s</tt> is null
1258 public static byte[] decode( String s, int options ) throws java.io.IOException {
1261 throw new NullPointerException( "Input string was null." );
1266 bytes = s.getBytes( PREFERRED_ENCODING );
1268 catch( java.io.UnsupportedEncodingException uee ) {
1269 bytes = s.getBytes();
1274 bytes = decode( bytes, 0, bytes.length, options );
1276 // Check to see if it's gzip-compressed
1277 // GZIP Magic Two-Byte Number: 0x8b1f (35615)
1278 boolean dontGunzip = (options & DONT_GUNZIP) != 0;
1279 if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) {
1281 int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
1282 if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) {
1283 java.io.ByteArrayInputStream bais = null;
1284 java.util.zip.GZIPInputStream gzis = null;
1285 java.io.ByteArrayOutputStream baos = null;
1286 byte[] buffer = new byte[2048];
1290 baos = new java.io.ByteArrayOutputStream();
1291 bais = new java.io.ByteArrayInputStream( bytes );
1292 gzis = new java.util.zip.GZIPInputStream( bais );
1294 while( ( length = gzis.read( buffer ) ) >= 0 ) {
1295 baos.write(buffer,0,length);
1296 } // end while: reading input
1298 // No error? Get new bytes.
1299 bytes = baos.toByteArray();
1302 catch( java.io.IOException e ) {
1303 e.printStackTrace();
1304 // Just return originally-decoded bytes
1307 try{ baos.close(); } catch( Exception e ){}
1308 try{ gzis.close(); } catch( Exception e ){}
1309 try{ bais.close(); } catch( Exception e ){}
1312 } // end if: gzipped
1313 } // end if: bytes.length >= 2
1321 * Attempts to decode Base64 data and deserialize a Java
1322 * Object within. Returns <tt>null</tt> if there was an error.
1324 * @param encodedObject The Base64 data to decode
1325 * @return The decoded and deserialized object
1326 * @throws NullPointerException if encodedObject is null
1327 * @throws java.io.IOException if there is a general error
1328 * @throws ClassNotFoundException if the decoded object is of a
1329 * class that cannot be found by the JVM
1332 public static Object decodeToObject( String encodedObject )
1333 throws java.io.IOException, java.lang.ClassNotFoundException {
1334 return decodeToObject(encodedObject,NO_OPTIONS,null);
1339 * Attempts to decode Base64 data and deserialize a Java
1340 * Object within. Returns <tt>null</tt> if there was an error.
1341 * If <tt>loader</tt> is not null, it will be the class loader
1342 * used when deserializing.
1344 * @param encodedObject The Base64 data to decode
1345 * @param options Various parameters related to decoding
1346 * @param loader Optional class loader to use in deserializing classes.
1347 * @return The decoded and deserialized object
1348 * @throws NullPointerException if encodedObject is null
1349 * @throws java.io.IOException if there is a general error
1350 * @throws ClassNotFoundException if the decoded object is of a
1351 * class that cannot be found by the JVM
1354 public static Object decodeToObject(
1355 String encodedObject, int options, final ClassLoader loader )
1356 throws java.io.IOException, java.lang.ClassNotFoundException {
1358 // Decode and gunzip if necessary
1359 byte[] objBytes = decode( encodedObject, options );
1361 java.io.ByteArrayInputStream bais = null;
1362 java.io.ObjectInputStream ois = null;
1366 bais = new java.io.ByteArrayInputStream( objBytes );
1368 // If no custom class loader is provided, use Java's builtin OIS.
1369 if( loader == null ){
1370 ois = new java.io.ObjectInputStream( bais );
1371 } // end if: no loader provided
1373 // Else make a customized object input stream that uses
1374 // the provided class loader.
1376 ois = new java.io.ObjectInputStream(bais){
1378 public Class<?> resolveClass(java.io.ObjectStreamClass streamClass)
1379 throws java.io.IOException, ClassNotFoundException {
1380 Class<?> c = Class.forName(streamClass.getName(), false, loader);
1382 return super.resolveClass(streamClass);
1384 return c; // Class loader knows of this class.
1385 } // end else: not null
1386 } // end resolveClass
1388 } // end else: no custom class loader
1390 obj = ois.readObject();
1392 catch( java.io.IOException e ) {
1393 throw e; // Catch and throw in order to execute finally{}
1395 catch( java.lang.ClassNotFoundException e ) {
1396 throw e; // Catch and throw in order to execute finally{}
1399 try{ bais.close(); } catch( Exception e ){}
1400 try{ ois.close(); } catch( Exception e ){}
1404 } // end decodeObject
1409 * Convenience method for encoding data to a file.
1411 * <p>As of v 2.3, if there is a error,
1412 * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
1413 * In earlier versions, it just returned false, but
1414 * in retrospect that's a pretty poor way to handle it.</p>
1416 * @param dataToEncode byte array of data to encode in base64 form
1417 * @param filename Filename for saving encoded data
1418 * @throws java.io.IOException if there is an error
1419 * @throws NullPointerException if dataToEncode is null
1422 public static void encodeToFile( byte[] dataToEncode, String filename )
1423 throws java.io.IOException {
1425 if( dataToEncode == null ){
1426 throw new NullPointerException( "Data to encode was null." );
1429 Base64.OutputStream bos = null;
1431 bos = new Base64.OutputStream(
1432 new java.io.FileOutputStream( filename ), Base64.ENCODE );
1433 bos.write( dataToEncode );
1435 catch( java.io.IOException e ) {
1436 throw e; // Catch and throw to execute finally{} block
1437 } // end catch: java.io.IOException
1439 try{ bos.close(); } catch( Exception e ){}
1442 } // end encodeToFile
1446 * Convenience method for decoding data to a file.
1448 * <p>As of v 2.3, if there is a error,
1449 * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
1450 * In earlier versions, it just returned false, but
1451 * in retrospect that's a pretty poor way to handle it.</p>
1453 * @param dataToDecode Base64-encoded data as a string
1454 * @param filename Filename for saving decoded data
1455 * @throws java.io.IOException if there is an error
1458 public static void decodeToFile( String dataToDecode, String filename )
1459 throws java.io.IOException {
1461 Base64.OutputStream bos = null;
1463 bos = new Base64.OutputStream(
1464 new java.io.FileOutputStream( filename ), Base64.DECODE );
1465 bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) );
1467 catch( java.io.IOException e ) {
1468 throw e; // Catch and throw to execute finally{} block
1469 } // end catch: java.io.IOException
1471 try{ bos.close(); } catch( Exception e ){}
1474 } // end decodeToFile
1480 * Convenience method for reading a base64-encoded
1481 * file and decoding it.
1483 * <p>As of v 2.3, if there is a error,
1484 * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
1485 * In earlier versions, it just returned false, but
1486 * in retrospect that's a pretty poor way to handle it.</p>
1488 * @param filename Filename for reading encoded data
1489 * @return decoded byte array
1490 * @throws java.io.IOException if there is an error
1493 public static byte[] decodeFromFile( String filename )
1494 throws java.io.IOException {
1496 byte[] decodedData = null;
1497 Base64.InputStream bis = null;
1500 // Set up some useful variables
1501 java.io.File file = new java.io.File( filename );
1502 byte[] buffer = null;
1506 // Check for size of file
1507 if( file.length() > Integer.MAX_VALUE )
1509 throw new java.io.IOException( "File is too big for this convenience method (" + file.length() + " bytes)." );
1510 } // end if: file too big for int index
1511 buffer = new byte[ (int)file.length() ];
1514 bis = new Base64.InputStream(
1515 new java.io.BufferedInputStream(
1516 new java.io.FileInputStream( file ) ), Base64.DECODE );
1519 while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) {
1523 // Save in a variable to return
1524 decodedData = new byte[ length ];
1525 System.arraycopy( buffer, 0, decodedData, 0, length );
1528 catch( java.io.IOException e ) {
1529 throw e; // Catch and release to execute finally{}
1530 } // end catch: java.io.IOException
1532 try{ bis.close(); } catch( Exception e) {}
1536 } // end decodeFromFile
1541 * Convenience method for reading a binary file
1542 * and base64-encoding it.
1544 * <p>As of v 2.3, if there is a error,
1545 * the method will throw an java.io.IOException. <b>This is new to v2.3!</b>
1546 * In earlier versions, it just returned false, but
1547 * in retrospect that's a pretty poor way to handle it.</p>
1549 * @param filename Filename for reading binary data
1550 * @return base64-encoded string
1551 * @throws java.io.IOException if there is an error
1554 public static String encodeFromFile( String filename )
1555 throws java.io.IOException {
1557 String encodedData = null;
1558 Base64.InputStream bis = null;
1561 // Set up some useful variables
1562 java.io.File file = new java.io.File( filename );
1563 byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4+1),40) ]; // Need max() for math on small files (v2.2.1); Need +1 for a few corner cases (v2.3.5)
1568 bis = new Base64.InputStream(
1569 new java.io.BufferedInputStream(
1570 new java.io.FileInputStream( file ) ), Base64.ENCODE );
1573 while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) {
1577 // Save in a variable to return
1578 encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING );
1581 catch( java.io.IOException e ) {
1582 throw e; // Catch and release to execute finally{}
1583 } // end catch: java.io.IOException
1585 try{ bis.close(); } catch( Exception e) {}
1589 } // end encodeFromFile
1592 * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
1594 * @param infile Input file
1595 * @param outfile Output file
1596 * @throws java.io.IOException if there is an error
1599 public static void encodeFileToFile( String infile, String outfile )
1600 throws java.io.IOException {
1602 String encoded = Base64.encodeFromFile( infile );
1603 java.io.OutputStream out = null;
1605 out = new java.io.BufferedOutputStream(
1606 new java.io.FileOutputStream( outfile ) );
1607 out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output.
1609 catch( java.io.IOException e ) {
1610 throw e; // Catch and release to execute finally{}
1613 try { out.close(); }
1614 catch( Exception ex ){}
1616 } // end encodeFileToFile
1620 * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
1622 * @param infile Input file
1623 * @param outfile Output file
1624 * @throws java.io.IOException if there is an error
1627 public static void decodeFileToFile( String infile, String outfile )
1628 throws java.io.IOException {
1630 byte[] decoded = Base64.decodeFromFile( infile );
1631 java.io.OutputStream out = null;
1633 out = new java.io.BufferedOutputStream(
1634 new java.io.FileOutputStream( outfile ) );
1635 out.write( decoded );
1637 catch( java.io.IOException e ) {
1638 throw e; // Catch and release to execute finally{}
1641 try { out.close(); }
1642 catch( Exception ex ){}
1644 } // end decodeFileToFile
1647 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
1652 * A {@link Base64.InputStream} will read data from another
1653 * <tt>java.io.InputStream</tt>, given in the constructor,
1654 * and encode/decode to/from Base64 notation on the fly.
1659 public static class InputStream extends java.io.FilterInputStream {
1661 private boolean encode; // Encoding or decoding
1662 private int position; // Current position in the buffer
1663 private byte[] buffer; // Small buffer holding converted data
1664 private int bufferLength; // Length of buffer (3 or 4)
1665 private int numSigBytes; // Number of meaningful bytes in the buffer
1666 private int lineLength;
1667 private boolean breakLines; // Break lines at less than 80 characters
1668 private int options; // Record options used to create the stream.
1669 private byte[] decodabet; // Local copies to avoid extra method calls
1673 * Constructs a {@link Base64.InputStream} in DECODE mode.
1675 * @param in the <tt>java.io.InputStream</tt> from which to read data.
1678 public InputStream( java.io.InputStream in ) {
1680 } // end constructor
1684 * Constructs a {@link Base64.InputStream} in
1685 * either ENCODE or DECODE mode.
1687 * Valid options:<pre>
1688 * ENCODE or DECODE: Encode or Decode as data is read.
1689 * DO_BREAK_LINES: break lines at 76 characters
1690 * (only meaningful when encoding)</i>
1693 * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1696 * @param in the <tt>java.io.InputStream</tt> from which to read data.
1697 * @param options Specified options
1698 * @see Base64#ENCODE
1699 * @see Base64#DECODE
1700 * @see Base64#DO_BREAK_LINES
1703 public InputStream( java.io.InputStream in, int options ) {
1706 this.options = options; // Record for later
1707 this.breakLines = (options & DO_BREAK_LINES) > 0;
1708 this.encode = (options & ENCODE) > 0;
1709 this.bufferLength = encode ? 4 : 3;
1710 this.buffer = new byte[ bufferLength ];
1712 this.lineLength = 0;
1713 this.decodabet = getDecodabet(options);
1714 } // end constructor
1717 * Reads enough of the input stream to convert
1718 * to/from Base64 and returns the next byte.
1724 public int read() throws java.io.IOException {
1726 // Do we need to get data?
1727 if( position < 0 ) {
1729 byte[] b3 = new byte[3];
1730 int numBinaryBytes = 0;
1731 for( int i = 0; i < 3; i++ ) {
1734 // If end of stream, b is -1.
1739 break; // out of for loop
1740 } // end else: end of stream
1742 } // end for: each needed input byte
1744 if( numBinaryBytes > 0 ) {
1745 encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
1748 } // end if: got data
1750 return -1; // Must be end of stream
1752 } // end if: encoding
1756 byte[] b4 = new byte[4];
1758 for( i = 0; i < 4; i++ ) {
1759 // Read four "meaningful" bytes:
1761 do{ b = in.read(); }
1762 while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
1765 break; // Reads a -1 if end of stream
1766 } // end if: end of stream
1769 } // end for: each needed input byte
1772 numSigBytes = decode4to3( b4, 0, buffer, 0, options );
1774 } // end if: got four characters
1777 } // end else if: also padded correctly
1779 // Must have broken out from above.
1780 throw new java.io.IOException( "Improperly padded Base64 input." );
1783 } // end else: decode
1784 } // end else: get data
1787 if( position >= 0 ) {
1788 // End of relevant data?
1789 if( /*!encode &&*/ position >= numSigBytes ){
1791 } // end if: got data
1793 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) {
1798 lineLength++; // This isn't important when decoding
1799 // but throwing an extra "if" seems
1800 // just as wasteful.
1802 int b = buffer[ position++ ];
1804 if( position >= bufferLength ) {
1808 return b & 0xFF; // This is how you "cast" a byte that's
1809 // intended to be unsigned.
1811 } // end if: position >= 0
1815 throw new java.io.IOException( "Error in Base64 code reading stream." );
1821 * Calls {@link #read()} repeatedly until the end of stream
1822 * is reached or <var>len</var> bytes are read.
1823 * Returns number of bytes read into array or -1 if
1824 * end of stream is encountered.
1826 * @param dest array to hold values
1827 * @param off offset for array
1828 * @param len max number of bytes to read into array
1829 * @return bytes read into array or -1 if end of stream is encountered.
1833 public int read( byte[] dest, int off, int len )
1834 throws java.io.IOException {
1837 for( i = 0; i < len; i++ ) {
1841 dest[off + i] = (byte) b;
1847 break; // Out of 'for' loop
1848 } // Out of 'for' loop
1849 } // end for: each byte read
1853 } // end inner class InputStream
1860 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1865 * A {@link Base64.OutputStream} will write data to another
1866 * <tt>java.io.OutputStream</tt>, given in the constructor,
1867 * and encode/decode to/from Base64 notation on the fly.
1872 public static class OutputStream extends java.io.FilterOutputStream {
1874 private boolean encode;
1875 private int position;
1876 private byte[] buffer;
1877 private int bufferLength;
1878 private int lineLength;
1879 private boolean breakLines;
1880 private byte[] b4; // Scratch used in a few places
1881 private boolean suspendEncoding;
1882 private int options; // Record for later
1883 private byte[] decodabet; // Local copies to avoid extra method calls
1886 * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1888 * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1891 public OutputStream( java.io.OutputStream out ) {
1892 this( out, ENCODE );
1893 } // end constructor
1897 * Constructs a {@link Base64.OutputStream} in
1898 * either ENCODE or DECODE mode.
1900 * Valid options:<pre>
1901 * ENCODE or DECODE: Encode or Decode as data is read.
1902 * DO_BREAK_LINES: don't break lines at 76 characters
1903 * (only meaningful when encoding)</i>
1906 * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1908 * @param out the <tt>java.io.OutputStream</tt> to which data will be written.
1909 * @param options Specified options.
1910 * @see Base64#ENCODE
1911 * @see Base64#DECODE
1912 * @see Base64#DO_BREAK_LINES
1915 public OutputStream( java.io.OutputStream out, int options ) {
1917 this.breakLines = (options & DO_BREAK_LINES) != 0;
1918 this.encode = (options & ENCODE) != 0;
1919 this.bufferLength = encode ? 3 : 4;
1920 this.buffer = new byte[ bufferLength ];
1922 this.lineLength = 0;
1923 this.suspendEncoding = false;
1924 this.b4 = new byte[4];
1925 this.options = options;
1926 this.decodabet = getDecodabet(options);
1927 } // end constructor
1931 * Writes the byte to the output stream after
1932 * converting to/from Base64 notation.
1933 * When encoding, bytes are buffered three
1934 * at a time before the output stream actually
1935 * gets a write() call.
1936 * When decoding, bytes are buffered four
1939 * @param theByte the byte to write
1943 public void write(int theByte)
1944 throws java.io.IOException {
1945 // Encoding suspended?
1946 if( suspendEncoding ) {
1947 this.out.write( theByte );
1949 } // end if: supsended
1953 buffer[ position++ ] = (byte)theByte;
1954 if( position >= bufferLength ) { // Enough to encode.
1956 this.out.write( encode3to4( b4, buffer, bufferLength, options ) );
1959 if( breakLines && lineLength >= MAX_LINE_LENGTH ) {
1960 this.out.write( NEW_LINE );
1962 } // end if: end of line
1965 } // end if: enough to output
1966 } // end if: encoding
1970 // Meaningful Base64 character?
1971 if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) {
1972 buffer[ position++ ] = (byte)theByte;
1973 if( position >= bufferLength ) { // Enough to output.
1975 int len = Base64.decode4to3( buffer, 0, b4, 0, options );
1976 out.write( b4, 0, len );
1978 } // end if: enough to output
1979 } // end if: meaningful base64 character
1980 else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) {
1981 throw new java.io.IOException( "Invalid character in Base64 data." );
1982 } // end else: not white space either
1983 } // end else: decoding
1989 * Calls {@link #write(int)} repeatedly until <var>len</var>
1990 * bytes are written.
1992 * @param theBytes array from which to read bytes
1993 * @param off offset for array
1994 * @param len max number of bytes to read into array
1998 public void write( byte[] theBytes, int off, int len )
1999 throws java.io.IOException {
2000 // Encoding suspended?
2001 if( suspendEncoding ) {
2002 this.out.write( theBytes, off, len );
2004 } // end if: supsended
2006 for( int i = 0; i < len; i++ ) {
2007 write( theBytes[ off + i ] );
2008 } // end for: each byte written
2015 * Method added by PHIL. [Thanks, PHIL. -Rob]
2016 * This pads the buffer without closing the stream.
2017 * @throws java.io.IOException if there's an error.
2019 public void flushBase64() throws java.io.IOException {
2020 if( position > 0 ) {
2022 out.write( encode3to4( b4, buffer, position, options ) );
2024 } // end if: encoding
2026 throw new java.io.IOException( "Base64 input not properly padded." );
2027 } // end else: decoding
2028 } // end if: buffer partially full
2034 * Flushes and closes (I think, in the superclass) the stream.
2039 public void close() throws java.io.IOException {
2040 // 1. Ensure that pending characters are written
2043 // 2. Actually close the stream
2044 // Base class both flushes and closes.
2054 * Suspends encoding of the stream.
2055 * May be helpful if you need to embed a piece of
2056 * base64-encoded data in a stream.
2058 * @throws java.io.IOException if there's an error flushing
2061 public void suspendEncoding() throws java.io.IOException {
2063 this.suspendEncoding = true;
2064 } // end suspendEncoding
2068 * Resumes encoding of the stream.
2069 * May be helpful if you need to embed a piece of
2070 * base64-encoded data in a stream.
2074 public void resumeEncoding() {
2075 this.suspendEncoding = false;
2076 } // end resumeEncoding
2080 } // end inner class OutputStream
2083 } // end class Base64