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