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