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