//============================================================================== // tribble/util/Base64Decoder.java //============================================================================== package tribble.util; // System imports import java.lang.IllegalArgumentException; import java.lang.String; import java.text.ParseException; // Local imports // (None) /******************************************************************************* * Base-64 decoding methods. * *
* Base-64 encoding (a.k.a. radix-64 encoding) is a form of encoding binary data * in 6-bit units. (6 bits provides 64 distinct binary values, hence the moniker * base-64 or radix-64.) Groups of three 8-bit octets (bytes), * totalling 24 bits, can be converted into four 6-bit units. Each 6-bit unit * can in turn be represented by one of 64 unique printable ASCII (8-bit) * characters. * *
* Converting straight binary data (as a sequence of 8-bit octets) into a * radix-64 representation is thus simply the the process of packing groups of * three 8-bit octets into four 6-bit units, and then substituting each 6-bit * unit with its corresponding ASCII character code. * *
* Converting radix-64 encoded data (as a sequence of 8-bit characters) back into * straight binary data is the reverse operation, substituting each radix-64 * ASCII character code with its corresponding 6-bit unit, then unpacking groups * of four 6-bit units into three 8-bit octets. * *
* A 65th special ASCII character is used to indicate trailing padding in * radix-64 encoded data. * *
* This class can be used as a simple replacement for the
* {@link sun.misc.BASE64Decoder} class.
*
*
* @version $Revision: 1.4 $ $Date: 2005/04/16 16:05:55 $
* @since 2003-02-26
* @author
* David R. Tribble
* (david@tribble.com).
*
*
* Copyright ©2003-2005 by David R. Tribble, all rights reserved.
*
* Permission is granted to freely use and distribute this source
* code provided that the original copyright and authorship notices
* remain intact.
*
* @see Base64Encoder
* @see sun.misc.BASE64Decoder
*/
public class Base64Decoder
{
// Identification
/** Revision information. */
static final String REV =
"@(#)tribble/util/Base64Decoder.java $Revision: 1.4 $ $Date: 2005/04/16 16:05:55 $\n";
/** Class revision number. */
public static final int SERIES = 104;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Public static methods
/***************************************************************************
* Decode base-64 characters into binary data.
*
*
*
*
* @param chars (const)
* Printable ASCII string containing binary data encoded as radix-64
* characters.
*
* @return
* Binary data, as an array of bytes (8-bit octets).
* This array will contain (chars.length()-p)*3/4 bytes,
* where p is 0, 1, or 2, such that the total input length is
* a multiple of 4.
*
* @throws ParseException
* Thrown if chars is malformed or contains an invalid character
* code.
*
* @see #decodeString(String, byte[], int) decodeString()
* @see Base64Encoder#encodeAsString(byte[])
* Base64Encoder.encodeAsString()
*
* @since 1.1, 2003-02-26
*/
public static byte[] decodeString(/*const*/ String chars)
throws ParseException
{
byte[] dec;
int len;
int pad;
int olen;
// Find the number of padding chars at the end
len = chars.length();
pad = 0;
if (chars.charAt(len-1) == '=')
pad++;
if (len >= 2 && chars.charAt(len-2) == '=')
pad++;
// Initial size estimate of the decoded 8-bit data
olen = len*3/4;
dec = new byte[olen-pad];
// Decode the radix-64 character string
decodeString(chars, dec, 0);
return (dec);
}
/***************************************************************************
* Decode base-64 characters into binary data.
*
*
* See {@link #decodeBytes(byte[], int, int, byte[], int) decodeBytes()} * for more details. * * * @param chars (const) * Printable ASCII string containing binary data encoded as radix-64 * characters. * * * @param dec * Decoded byte (8-bit octet) array, which is filled with the decoded data. * * @param off * Index of the first byte (8-bit octet) within dec to write to. * * @return * The number of bytes (8-bit octets) actually written into dec. * This will be (len+p)*3/4 bytes, * where p is 0, 1, or 2, such that the total input length is * a multiple of 4. * * @throws ParseException * Thrown if chars is malformed or contains an invalid character * code. * * @see #decodeString(String) decodeString() * @see Base64Encoder#encodeAsString(byte[], int, int) * Base64Encoder.encodeAsString() * * @since 1.1, 2003-02-26 */ public static int decodeString(/*const*/ String chars, byte[] dec, int off) throws ParseException { int len; int i; int o; // Skip padding characters at the end len = chars.length(); len += off-1; while (len >= 0 && chars.charAt(len) == '=') len--; len++; /// Note: Some day this should ignore whitespace and noise chars // Convert the base64 encoded chars into bytes, a chunk at a time i = off; o = off; while (i < len) { int s0; int s1; int s2 = 'A'; int s3 = 'A'; int d; // Convert a single 24-bit chunk (4 sextets into 3 octets) s0 = chars.charAt(i+0); s1 = chars.charAt(i+1); if (i+2 < len) { s2 = chars.charAt(i+2); if (i+3 < len) s3 = chars.charAt(i+3); } d = (fromBase64(s0 & 0xFF) << 18) | (fromBase64(s1 & 0xFF) << 12) | (fromBase64(s2 & 0xFF) << 6) | (fromBase64(s3 & 0xFF)); // Write the decoded binary octets into the output array dec[o++] = (byte) ((d >> 16) & 0xFF); if (i+2 < len) dec[o++] = (byte) ((d >> 8) & 0xFF); if (i+3 < len) dec[o++] = (byte) ((d) & 0xFF); i += 4; } // Done return (o - off); } /*************************************************************************** * Decode base-64 character bytes into binary data. * *
* See {@link #decodeBytes(byte[], int, int, byte[], int) decodeBytes()} * for more details. * * * @param chars (const) * An array of bytes containing printable ASCII characters, representating * binary data encoded as radix-64 characters. * * * @return * Binary data, as an array of bytes (8-bit octets). * This array will contain (chars.length+p)*3/4 bytes, * where p is 0, 1, or 2, such that the total input length is * a multiple of 4. * * @throws ParseException * Thrown if chars is malformed or contains an invalid character * code. * * @see #decodeBytes(byte[], int, int) decodeBytes() * @see Base64Encoder#encodeAsBytes(byte[], int, int) * Base64Encoder.encodeAsBytes() * * @since 1.4, 2005-04-16 */ public static byte[] decodeBytes(/*const*/ byte[] chars) throws ParseException { // Decode the radix-64 characters into binary data return (decodeBytes(chars, 0, chars.length)); } /*************************************************************************** * Decode base-64 character bytes into binary data. * *
* See {@link #decodeBytes(byte[], int, int, byte[], int) decodeBytes()} * for more details. * * * @param chars (const) * An array of bytes containing printable ASCII characters, representating * binary data encoded as radix-64 characters. * * * @param off * Index of the first byte (8-bit octet) within chars to decode. * * @param len * Number of bytes (8-bit octets) to decode from chars. * * @return * Binary data, as an array of bytes (8-bit octets). * This array will contain (len+p)*3/4 bytes, * where p is 0, 1, or 2, such that the total input length is * a multiple of 4. * * @throws ParseException * Thrown if chars is malformed or contains an invalid character * code. * * @see #decodeBytes(byte[], int, int, byte[], int) decodeBytes() * @see Base64Encoder#encodeAsBytes(byte[], int, int) * Base64Encoder.encodeAsBytes() * * @since 1.1, 2005-04-01 */ public static byte[] decodeBytes(/*const*/ byte[] chars, int off, int len) throws ParseException { int olen; int pad; byte[] dec; // Find the number of padding chars at the end len = chars.length; pad = 0; if (chars[len-1] == '=') pad++; if (len >= 2 && chars[len-2] == '=') pad++; /// Note: Some day this should handle whitespace and ignored chars // Allocate an output byte array olen = len/4*3 - pad; dec = new byte[olen]; // Decode the radix-64 characters into binary data decodeBytes(chars, off, len-pad, dec, 0); return (dec); } /*************************************************************************** * Decode base-64 character bytes into binary data. * *
* +-----------+-----------+-----------+-----------+ * | sextet | sextet | sextet | sextet | * |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0| input sextets * :- - - - - -:- -:- - - -:- - - -:- -:- - - - - -: * |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0| output octets * | octet | octet | octet | * +---------------+---------------+---------------+ * * +-----------+-----------+-----------+-----------+ * | sextet | sextet | sextet | padding | * |5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0| '=' | input sextets * :- - - - - -:- -:- - - -:- - - -:- -:- - - - - -: * |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|x x : output octets * | octet | octet | discarded : * +---------------+---------------+ - - - - - - - + * * +-----------+-----------+-----------+-----------+ * | sextet | sextet | padding | padding | * |5 4 3 2 1 0|5 4 3 2 1 0| '=' | '=' | input sextets * :- - - - - -:- -:- - - -:- - - -:- -:- - - - - -: * |7 6 5 4 3 2 1 0|x x x x : : output octets * | octet | discarded : discarded : * +---------------+ - - - - - - - + - - - - - - - +* *
* For example, the following table list some sextet sequences and their * resulting octet decodings: *
* 04,11,04,11 => 11,11,11 * 04,12,08,33 => 11,22,33 * 3F,3F,3F,3F => FF,FF,FF * 04,11,04, = => 11,11 * 04,11,07, = => 11,11 * 3F,3F,3C, = => FF,FF * 04,10, =, = => 11 * 04,1F, =, = => 11 * 3F,30, =, = => FF * 3F,30,03,3F,00,0F,3C,00 => FF,00,FF,00,FF,00* * * @param chars (const) * An array of bytes containing printable ASCII characters, representating * binary data encoded as radix-64 characters. * * * @param off * Index of the first byte (8-bit octet) within chars to decode. * * @param len * Number of bytes (8-bit octets) to decode from chars. * * @param dec * Decoded byte (8-bit octet) array, which is filled with the decoded data. * * @param decOff * Index of the first byte (8-bit octet) within dec to write to. * * @return * The number of bytes (8-bit octets) actually written into dec. * This will be (len+p)*3/4 bytes, * where p is 0, 1, or 2, such that the total input length is * a multiple of 4. * * @throws ParseException * Thrown if chars is malformed or contains an invalid character * code. * * @see Base64Encoder#encodeAsBytes(byte[], int, int) * Base64Encoder.encodeAsBytes() * * @since 1.1, 2003-02-26 */ public static int decodeBytes(/*const*/ byte[] chars, int off, int len, byte[] dec, int decOff) throws ParseException { int i; int o; // Skip padding characters at the end len += off-1; while (len >= 0 && chars[len] == '=') len--; len++; /// Note: Some day this should ignore whitespace and noise chars // Convert the base64 encoded chars into bytes, a chunk at a time i = off; o = decOff; while (i < len) { int s0; int s1; int s2 = 'A'; int s3 = 'A'; int d; // Convert a single 24-bit chunk (4 sextets into 3 octets) s0 = chars[i+0]; s1 = chars[i+1]; if (i+2 < len) { s2 = chars[i+2]; if (i+3 < len) s3 = chars[i+3]; } d = (fromBase64(s0 & 0xFF) << 18) | (fromBase64(s1 & 0xFF) << 12) | (fromBase64(s2 & 0xFF) << 6) | (fromBase64(s3 & 0xFF)); // Write the decoded binary octets into the output array dec[o++] = (byte) ((d >> 16) & 0xFF); if (i+2 < len) dec[o++] = (byte) ((d >> 8) & 0xFF); if (i+3 < len) dec[o++] = (byte) ((d) & 0xFF); i += 4; } // Done return (o - decOff); } /*************************************************************************** * Convert a radix-64 printable ASCII character code into its corresponding * 6-bit binary value. * * * @param ch * A printable radix-64 ASCII character code. * * @return * A 6-bit binary value in the range [0,63]; or the value 64, indicating the * special trailing padding code; or -1, indicating an invalid character. * * @see Base64Encoder#toBase64 Base64Encoder.toBase64() * * @since 1.1, 2003-02-26 */ public static int fromBase64(int ch) { // This averages 2.45 comparisons per call if (ch >= 'a') { if (ch <= 'z') return (ch-'a'+26); return (-1); } if (ch >= 'A') { if (ch <= 'Z') return (ch-'A'+0); return (-1); } if (ch >= '0') { if (ch <= '9') return (ch-'0'+52); if (ch == '=') return (64); return (-1); } if (ch == '+') return (62); if (ch == '/') return (63); return (-1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Variables // (None) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public constructors /*************************************************************************** * Default constructor. * *
* This constructor is provided for compatibility with the * {@link sun.misc.BASE64Decoder} class, which allows this class to be used * as a simple replacement for it. * * @since 1.2, 2005-04-10 */ public Base64Decoder() { // Do nothing } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Public methods /*************************************************************************** * Decode base-64 characters into binary data. * *
* This method is provided for compatibility, producing the same result as * the decodeBuffer() method of the {@link sun.misc.BASE64Decoder} * class. * * * * * @param chars (const) * Printable ASCII string containing binary data encoded as radix-64 * characters. * * @return * Binary data, as an array of bytes (8-bit octets). * This array will contain (chars.length()-p)*3/4 bytes, * where p is 0, 1, or 2, such that the total input length is * a multiple of 4. * * @throws IllegalArgumentException (unchecked) * Thrown if chars is malformed or contains an invalid character * code. * * @see #decodeString(String) decodeString() * @see Base64Encoder#encode(byte[]) Base64Encoder.encode() * * @since 1.2, 2005-04-10 */ public byte[] decodeBuffer(/*const*/ String chars) { try { // Decode the string of base-64 characters into binary data return (decodeString(chars)); } catch (ParseException ex) { // Ignore, convert into a IllegalArgumentException throw new IllegalArgumentException(ex.getMessage()); } } } // End Base64Decoder.java