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