FileDocCategorySizeDatePackage
Base64.javaAPI DocAndroid 1.5 API29806Wed May 06 22:42:46 BST 2009com.android.email.codec.binary

Base64

public class Base64 extends Object implements BinaryEncoder, BinaryDecoder
Provides Base64 encoding and decoding as defined by RFC 2045.

This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein.

see
RFC 2045
author
Apache Software Foundation
since
1.0-dev
version
$Id$

Fields Summary
static final int
CHUNK_SIZE
Chunk size per RFC 2045 section 6.8.

The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any equal signs.

static final byte[]
CHUNK_SEPARATOR
Chunk separator per RFC 2045 section 2.1.
private static final byte[]
intToBase64
This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" equivalents as specified in Table 1 of RFC 2045. Thanks to "commons" project in ws.apache.org for this code. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
private static final byte
PAD
Byte used to pad output.
private static final byte[]
base64ToInt
This array is a lookup table that translates unicode characters drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 alphabet but fall within the bounds of the array are translated to -1. Thanks to "commons" project in ws.apache.org for this code. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
private static final int
MASK_6BITS
Mask used to extract 6 bits, used when encoding
private static final int
MASK_8BITS
Mask used to extract 8 bits, used in decoding base64 bytes
private final int
lineLength
Line length for encoding. Not used when decoding. A value of zero or less implies no chunking of the base64 encoded data.
private final byte[]
lineSeparator
Line separator for encoding. Not used when decoding. Only used if lineLength > 0.
private final int
decodeSize
Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. decodeSize = 3 + lineSeparator.length;
private final int
encodeSize
Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. encodeSize = 4 + lineSeparator.length;
private byte[]
buf
Buffer for streaming.
private int
pos
Position where next character should be written in the buffer.
private int
readPos
Position where next character should be read from the buffer.
private int
currentLinePos
Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to make sure each encoded line never goes beyond lineLength (if lineLength > 0).
private int
modulus
Writes to the buffer only occur after every 3 reads when encoding, an every 4 reads when decoding. This variable helps track that.
private boolean
eof
Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this Base64 object becomes useless, and must be thrown away.
private int
x
Place holder for the 3 bytes we're dealing with for our base64 logic. Bitwise operations store and extract the base64 encoding or decoding from this variable.
Constructors Summary
public Base64()
Default constructor: lineLength is 76, and the lineSeparator is CRLF when encoding, and all forms can be decoded.


                            
      
        this(CHUNK_SIZE, CHUNK_SEPARATOR);
    
public Base64(int lineLength)

Consumer can use this constructor to choose a different lineLength when encoding (lineSeparator is still CRLF). All forms of data can be decoded.

Note: lineLengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.

param
lineLength each line of encoded data will be at most this long (rounded up to nearest multiple of 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding.

        this(lineLength, CHUNK_SEPARATOR);
    
public Base64(int lineLength, byte[] lineSeparator)

Consumer can use this constructor to choose a different lineLength and lineSeparator when encoding. All forms of data can be decoded.

Note: lineLengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.

param
lineLength Each line of encoded data will be at most this long (rounded up to nearest multiple of 4). Ignored when decoding. If <= 0, then output will not be divided into lines (chunks).
param
lineSeparator Each line of encoded data will end with this sequence of bytes. If lineLength <= 0, then the lineSeparator is not used.
throws
IllegalArgumentException The provided lineSeparator included some base64 characters. That's not going to work!

        this.lineLength = lineLength;
        this.lineSeparator = new byte[lineSeparator.length];
        System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
        if (lineLength > 0) {
            this.encodeSize = 4 + lineSeparator.length;
        } else {
            this.encodeSize = 4;
        }
        this.decodeSize = encodeSize - 1;
        if (containsBase64Byte(lineSeparator)) {
            String sep;
            try {
                sep = new String(lineSeparator, "UTF-8");
            } catch (UnsupportedEncodingException uee) {
                sep = new String(lineSeparator);
            }
            throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");
        }
    
Methods Summary
intavail()
Returns the amount of buffered data available for reading.

return
The amount of buffered data available for reading.

 return buf != null ? pos - readPos : 0; 
private static booleancontainsBase64Byte(byte[] arrayOctet)

        for (int i = 0; i < arrayOctet.length; i++) {
            if (isBase64(arrayOctet[i])) {
                return true;
            }
        }
        return false;
    
voiddecode(byte[] in, int inPos, int inAvail)

Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" call is not necessary when decoding, but it doesn't hurt, either.

Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, garbage-out philosophy: it will not check the provided data for validity.

Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/

param
in byte[] array of ascii data to base64 decode.
param
inPos Position to start reading data from.
param
inAvail Amount of bytes available from input for encoding.

        if (eof) {
            return;
        }
        if (inAvail < 0) {
            eof = true;
        }
        for (int i = 0; i < inAvail; i++) {
            if (buf == null || buf.length - pos < decodeSize) {
                resizeBuf();
            }
            byte b = in[inPos++];
            if (b == PAD) {
                x = x << 6;
                switch (modulus) {
                    case 2:
                        x = x << 6;
                        buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
                        break;
                    case 3:
                        buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
                        buf[pos++] = (byte) ((x >> 8) & MASK_8BITS);
                        break;
                }
                // WE'RE DONE!!!!
                eof = true;
                return;
            } else {
                if (b >= 0 && b < base64ToInt.length) {
                    int result = base64ToInt[b];
                    if (result >= 0) {
                        modulus = (++modulus) % 4;
                        x = (x << 6) + result;
                        if (modulus == 0) {
                            buf[pos++] = (byte) ((x >> 16) & MASK_8BITS);
                            buf[pos++] = (byte) ((x >> 8) & MASK_8BITS);
                            buf[pos++] = (byte) (x & MASK_8BITS);
                        }
                    }
                }
            }
        }
    
public java.lang.Objectdecode(java.lang.Object pObject)
Decodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[].

param
pObject Object to decode
return
An object (of type byte[]) containing the binary data which corresponds to the byte[] supplied.
throws
DecoderException if the parameter supplied is not of type byte[]

        if (!(pObject instanceof byte[])) {
            throw new DecoderException("Parameter supplied to Base64 decode is not a byte[]");
        }
        return decode((byte[]) pObject);
    
public byte[]decode(byte[] pArray)
Decodes a byte[] containing containing characters in the Base64 alphabet.

param
pArray A byte array containing Base64 character data
return
a byte array containing binary data

        return decodeBase64(pArray);
    
public static byte[]decodeBase64(byte[] base64Data)
Decodes Base64 data into octets

param
base64Data Byte array containing Base64 data
return
Array containing decoded data.

        if (base64Data == null || base64Data.length == 0) {
            return base64Data;
        }
        Base64 b64 = new Base64();

        long len = (base64Data.length * 3) / 4;
        byte[] buf = new byte[(int) len];
        b64.setInitialBuffer(buf, 0, buf.length);
        b64.decode(base64Data, 0, base64Data.length);
        b64.decode(base64Data, 0, -1); // Notify decoder of EOF.

        // We have no idea what the line-length was, so we
        // cannot know how much of our array wasn't used.
        byte[] result = new byte[b64.pos];
        b64.readResults(result, 0, result.length);
        return result;
    
public static java.math.BigIntegerdecodeInteger(byte[] pArray)
Decode a byte64-encoded integer according to crypto standards such as W3C's XML-Signature

param
pArray a byte array containing base64 character data
return
A BigInteger

        return new BigInteger(1, decodeBase64(pArray));
    
static byte[]discardNonBase64(byte[] data)
Discards any characters outside of the base64 alphabet, per the requirements on page 25 of RFC 2045 - "Any characters outside of the base64 alphabet are to be ignored in base64 encoded data."

param
data The base-64 encoded data to groom
return
The data, less non-base64 characters (see RFC 2045).

        byte groomedData[] = new byte[data.length];
        int bytesCopied = 0;

        for (int i = 0; i < data.length; i++) {
            if (isBase64(data[i])) {
                groomedData[bytesCopied++] = data[i];
            }
        }

        byte packedData[] = new byte[bytesCopied];

        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);

        return packedData;
    
static byte[]discardWhitespace(byte[] data)
Discards any whitespace from a base-64 encoded block.

param
data The base-64 encoded data to discard the whitespace from.
return
The data, less whitespace (see RFC 2045).
deprecated
This method is no longer needed

        byte groomedData[] = new byte[data.length];
        int bytesCopied = 0;

        for (int i = 0; i < data.length; i++) {
            switch (data[i]) {
                case ' " :
                case '\n" :
                case '\r" :
                case '\t" :
                    break;
                default :
                    groomedData[bytesCopied++] = data[i];
            }
        }

        byte packedData[] = new byte[bytesCopied];

        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);

        return packedData;
    
public java.lang.Objectencode(java.lang.Object pObject)
Encodes an Object using the base64 algorithm. This method is provided in order to satisfy the requirements of the Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[].

param
pObject Object to encode
return
An object (of type byte[]) containing the base64 encoded data which corresponds to the byte[] supplied.
throws
EncoderException if the parameter supplied is not of type byte[]

        if (!(pObject instanceof byte[])) {
            throw new EncoderException("Parameter supplied to Base64 encode is not a byte[]");
        }
        return encode((byte[]) pObject);
    
public byte[]encode(byte[] pArray)
Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet.

param
pArray a byte array containing binary data
return
A byte array containing only Base64 character data

        return encodeBase64(pArray, false);
    
voidencode(byte[] in, int inPos, int inAvail)

Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last remaining bytes (if not multiple of 3).

Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/

param
in byte[] array of binary data to base64 encode.
param
inPos Position to start reading data from.
param
inAvail Amount of bytes available from input for encoding.

        if (eof) {
            return;
        }

        // inAvail < 0 is how we're informed of EOF in the underlying data we're
        // encoding.
        if (inAvail < 0) {
            eof = true;
            if (buf == null || buf.length - pos < encodeSize) {
                resizeBuf();
            }
            switch (modulus) {
                case 1:
                    buf[pos++] = intToBase64[(x >> 2) & MASK_6BITS];
                    buf[pos++] = intToBase64[(x << 4) & MASK_6BITS];
                    buf[pos++] = PAD;
                    buf[pos++] = PAD;
                    break;

                case 2:
                    buf[pos++] = intToBase64[(x >> 10) & MASK_6BITS];
                    buf[pos++] = intToBase64[(x >> 4) & MASK_6BITS];
                    buf[pos++] = intToBase64[(x << 2) & MASK_6BITS];
                    buf[pos++] = PAD;
                    break;
            }
            if (lineLength > 0) {
                System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
                pos += lineSeparator.length;
            }
        } else {
            for (int i = 0; i < inAvail; i++) {
                if (buf == null || buf.length - pos < encodeSize) {
                    resizeBuf();
                }
                modulus = (++modulus) % 3;
                int b = in[inPos++];
                if (b < 0) { b += 256; }
                x = (x << 8) + b;
                if (0 == modulus) {
                    buf[pos++] = intToBase64[(x >> 18) & MASK_6BITS];
                    buf[pos++] = intToBase64[(x >> 12) & MASK_6BITS];
                    buf[pos++] = intToBase64[(x >> 6) & MASK_6BITS];
                    buf[pos++] = intToBase64[x & MASK_6BITS];
                    currentLinePos += 4;
                    if (lineLength > 0 && lineLength <= currentLinePos) {
                        System.arraycopy(lineSeparator, 0, buf, pos, lineSeparator.length);
                        pos += lineSeparator.length;
                        currentLinePos = 0;
                    }
                }
            }
        }
    
public static byte[]encodeBase64(byte[] binaryData)
Encodes binary data using the base64 algorithm but does not chunk the output.

param
binaryData binary data to encode
return
Base64 characters

        return encodeBase64(binaryData, false);
    
public static byte[]encodeBase64(byte[] binaryData, boolean isChunked)
Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.

param
binaryData Array containing binary data to encode.
param
isChunked if true this encoder will chunk the base64 output into 76 character blocks
return
Base64-encoded data.
throws
IllegalArgumentException Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}

        if (binaryData == null || binaryData.length == 0) {
            return binaryData;
        }
        Base64 b64 = isChunked ? new Base64() : new Base64(0);

        long len = (binaryData.length * 4) / 3;
        long mod = len % 4;
        if (mod != 0) {
            len += 4 - mod;
        }
        // If chunked, add space for one CHUNK_SEPARATOR per chunk.  (Technically, these are chunk
        // terminators, because even a single chunk message has one.)
        //
        //  User length     Encoded length      Rounded up by 4     Num chunks     Final buf len
        //      56              74                  76                  1               78
        //      57              76                  76                  1               78
        //      58              77                  80                  2               84
        //      59              78                  80                  2               84
        //
        // Or...
        //    Rounded up size:   4...76    Chunks:  1
        //    Rounded up size:  80..152    Chunks:  2
        //    Rounded up size: 156..228    Chunks:  3     ...etc...
        if (isChunked) {
            len += ((len + CHUNK_SIZE - 1) / CHUNK_SIZE) * CHUNK_SEPARATOR.length;
        }

        if (len > Integer.MAX_VALUE) {
            throw new IllegalArgumentException(
                    "Input array too big, output array would be bigger than Integer.MAX_VALUE=" + Integer.MAX_VALUE);
        }
        byte[] buf = new byte[(int) len];
        b64.setInitialBuffer(buf, 0, buf.length);
        b64.encode(binaryData, 0, binaryData.length);
        b64.encode(binaryData, 0, -1); // Notify encoder of EOF.

        // Encoder might have resized, even though it was unnecessary.
        if (b64.buf != buf) {
            b64.readResults(buf, 0, buf.length);
        }
        return buf;
    
public static byte[]encodeBase64Chunked(byte[] binaryData)
Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks

param
binaryData binary data to encode
return
Base64 characters chunked in 76 character blocks

        return encodeBase64(binaryData, true);
    
public static byte[]encodeInteger(java.math.BigInteger bigInt)
Encode to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature

param
bigInt a BigInteger
return
A byte array containing base64 character data
throws
NullPointerException if null is passed in

        if(bigInt == null)  {
            throw new NullPointerException("encodeInteger called with null parameter");
        }

        return encodeBase64(toIntegerBytes(bigInt), false);
    
booleanhasData()
Returns true if this Base64 object has buffered data for reading.

return
true if there is Base64 object still available for reading.

 return buf != null; 
public static booleanisArrayByteBase64(byte[] arrayOctet)
Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the method treats whitespace as valid.

param
arrayOctet byte array to test
return
true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; false, otherwise

        for (int i = 0; i < arrayOctet.length; i++) {
            if (!isBase64(arrayOctet[i]) && !isWhiteSpace(arrayOctet[i])) {
                return false;
            }
        }
        return true;
    
public static booleanisBase64(byte octet)
Returns whether or not the octet is in the base 64 alphabet.

param
octet The value to test
return
true if the value is defined in the the base 64 alphabet, false otherwise.

        return octet == PAD || (octet >= 0 && octet < base64ToInt.length && base64ToInt[octet] != -1);
    
private static booleanisWhiteSpace(byte byteToCheck)
Check if a byte value is whitespace or not.

param
byteToCheck the byte to check
return
true if byte is whitespace, false otherwise

        switch (byteToCheck) {
        case ' " :
        case '\n" :
        case '\r" :
        case '\t" :
            return true;
        default :
            return false;
        }
    
intreadResults(byte[] b, int bPos, int bAvail)
Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail bytes. Returns how many bytes were actually extracted.

param
b byte[] array to extract the buffered data into.
param
bPos position in byte[] array to start extraction at.
param
bAvail amount of bytes we're allowed to extract. We may extract fewer (if fewer are available).
return
The number of bytes successfully extracted into the provided byte[] array.

        if (buf != null) {
            int len = Math.min(avail(), bAvail);
            if (buf != b) {
                System.arraycopy(buf, readPos, b, bPos, len);
                readPos += len;
                if (readPos >= pos) {
                    buf = null;
                }
            } else {
                // Re-using the original consumer's output array is only
                // allowed for one round.
                buf = null;
            }
            return len;
        } else {
            return eof ? -1 : 0;
        }
    
private voidresizeBuf()
Doubles our buffer.

        if (buf == null) {
            buf = new byte[8192];
            pos = 0;
            readPos = 0;
        } else {
            byte[] b = new byte[buf.length * 2];
            System.arraycopy(buf, 0, b, 0, buf.length);
            buf = b;
        }
    
voidsetInitialBuffer(byte[] out, int outPos, int outAvail)
Small optimization where we try to buffer directly to the consumer's output array for one round (if consumer calls this method first!) instead of starting our own buffer.

param
out byte[] array to buffer directly to.
param
outPos Position to start buffering into.
param
outAvail Amount of bytes available for direct buffering.

        // We can re-use consumer's original output array under
        // special circumstances, saving on some System.arraycopy().
        if (out != null && out.length == outAvail) {
            buf = out;
            pos = outPos;
            readPos = outPos;
        }
    
static byte[]toIntegerBytes(java.math.BigInteger bigInt)
Returns a byte-array representation of a BigInteger without sign bit.

param
bigInt BigInteger to be converted
return
a byte array representation of the BigInteger parameter

        int bitlen = bigInt.bitLength();
        // round bitlen
        bitlen = ((bitlen + 7) >> 3) << 3;
        byte[] bigBytes = bigInt.toByteArray();

        if(((bigInt.bitLength() % 8) != 0) &&
            (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
            return bigBytes;
        }

        // set up params for copying everything but sign bit
        int startSrc = 0;
        int len = bigBytes.length;

        // if bigInt is exactly byte-aligned, just skip signbit in copy
        if((bigInt.bitLength() % 8) == 0) {
            startSrc = 1;
            len--;
        }

        int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
        byte[] resizedBytes = new byte[bitlen / 8];

        System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);

        return resizedBytes;