Here's my version - it should be completely standalone:
public class Base64
{
// *****************************************************************************
// INSTANCE PROPERTIES
// *****************************************************************************
private String lineSeparator;
private int lineLength;
// *****************************************************************************
// INSTANCE CONSTRUCTION/INITIALIZATON/FINALIZATION, OPEN/CLOSE
// *****************************************************************************
public Base64() {
lineSeparator=System.getProperty("line.separator");
lineLength=0;
}
// *****************************************************************************
// INSTANCE METHODS
// *****************************************************************************
public String encode(byte[] bin) {
return encode(bin,0,bin.length);
}
/**
* Encode an array of bytes as Base64.
* It will be broken into lines if the line length is not 0. If broken into lines,
* the last line is not terminated with a line separator.
*
* param ba The byte array to encode.
*/
public String encode(byte[] bin, int str, int len) {
int ol; // output length
StringBuffer sb; // string buffer for output(must be local for recursion to work).
int lp; // line position(must be local for recursion to work).
int el; // even multiple of 3 length
int ll; // leftover length
// CREATE OUTPUT BUFFER
ol=(((len+2)/3)*4);
if(lineLength!=0) {
int lines=(((ol+lineLength-1)/lineLength)-1);
if(lines>0) { ol+=(lines*lineSeparator.length()); }
}
sb=new StringBuffer(ol);
lp=0;
// EVEN MULTIPLES OF 3
el=(len/3)*3;
ll=(len-el);
for(int xa=0; xa<el; xa+=3) {
int cv;
int c0,c1,c2,c3;
if(lineLength!=0) {
lp+=4;
if(lp>lineLength) { sb.append(lineSeparator); lp=4; }
}
// get next three bytes in unsigned form lined up, in big-endian order
cv =(bin[xa+str+0]&0xFF); cv<<=8;
cv|=(bin[xa+str+1]&0xFF); cv<<=8;
cv|=(bin[xa+str+2]&0xFF);
// break those 24 bits into a 4 groups of 6 bits, working LSB to MSB.
c3=cv&0x3F; cv >>>=6;
c2=cv&0x3F; cv >>>=6;
c1=cv&0x3F; cv >>>=6;
c0=cv&0x3F;
// Translate into the equivalent alpha character emitting them in big-endian order.
sb.append(ENCODE[c0]);
sb.append(ENCODE[c1]);
sb.append(ENCODE[c2]);
sb.append(ENCODE[c3]);
}
// LEFTOVERS
if(lineLength!=0 && ll>0) {
lp+=4;
if(lp>lineLength) { sb.append(lineSeparator); lp=4; }
}
if(ll==1) {
sb.append(encode(new byte[] { bin[el+str], 0, 0 }).substring(0,2)).append("=="); // Use recursion so escaping logic is not repeated, replacing last 2 chars with "==".
}
else if(ll==2) {
sb.append(encode(new byte[] { bin[el+str], bin[el+str+1], 0 }).substring(0,3)).append("="); // Use recursion so escaping logic is not repeated, replacing last char and "=".
}
if(ol!=sb.length()) { throw new RuntimeException("Error in Base64 encoding method: Calculated output length of "+ol+" did not match actual length of "+sb.length()); }
return sb.toString();
}
public byte[] decode(String b64) {
return decode(b64,0,b64.length());
}
/**
* Decode a Base64 string to an array of bytes.
* The string must have a length evenly divisible by 4 (not counting line separators and other
* ignorable characters, like whitespace).
*/
public byte[] decode(String b64, int str, int len) {
byte[] ba; // target byte array
int dc; // decode cycle (within sequence of 4 input chars).
int rv; // reconstituted value
int ol; // output length
int pc; // padding count
ba=new byte[(len/4)*3]; // create array to largest possible size.
dc=0;
rv=0;
ol=0;
pc=0;
for(int xa=0; xa<len; xa++) {
int ch=b64.charAt(xa+str);
int value=(ch<=255 ? DECODE[ch] : IGNORE);
if(value!=IGNORE) {
if(value==PAD) {
value=0;
pc++;
}
switch(dc) {
case 0: {
rv=value;
dc=1;
} break;
case 1: {
rv<<=6;
rv|=value;
dc=2;
} break;
case 2: {
rv<<=6;
rv|=value;
dc=3;
} break;
case 3: {
rv<<=6;
rv|=value;
// Completed a cycle of 4 chars, so recombine the four 6-bit values in big-endian order
ba[ol+2]=(byte)rv; rv>>>=8;
ba[ol+1]=(byte)rv; rv>>>=8;
ba[ol]=(byte)rv; ol+=3;
dc=0;
} break;
}
}
}
if(dc!=0) {
throw new ArrayIndexOutOfBoundsException("Base64 data given as input was not an even multiple of 4 characters (should be padded with '=' characters).");
}
ol-=pc;
if(ba.length!=ol) {
byte[] b2=new byte[ol];
System.arraycopy(ba, 0, b2, 0, ol);
ba=b2;
}
return ba;
}
/**
* Set maximum line length for encoded lines.
* Ignored by decode.
* @param len Length of each line. 0 means no newlines inserted. Must be a multiple of 4.
*/
public void setLineLength(int len) {
this.lineLength=(len/4)*4;
}
/**
* Set the line separator sequence for encoded lines.
* Ignored by decode.
* Usually contains only a combination of chars \n and \r, but could be any chars except 'A'-'Z', 'a'-'z', '0'-'9', '+' and '/'.
* @param linsep Line separator - may be "" but not null.
*/
public void setLineSeparator(String linsep) {
this.lineSeparator=linsep;
}
// *****************************************************************************
// STATIC PROPERTIES
// *****************************************************************************
static private final char[] ENCODE=new char[64]; // translation array for encoding
static private final int[] DECODE=new int[256]; // translation array for decoding
static private final int IGNORE=-1; // flag for values to ignore when decoding
static private final int PAD=-2; // flag value for padding value when decoding
static private final Base64 BASE64=new Base64(); // default converter
// *****************************************************************************
// STATIC INIT & MAIN
// *****************************************************************************
static {
for(int xa=0; xa<=25; xa++) { ENCODE[xa]=(char)('A'+xa); } // 0..25 -> 'A'..'Z'
for(int xa=0; xa<=25; xa++) { ENCODE[xa+26]=(char)('a'+xa); } // 26..51 -> 'a'..'z'
for(int xa=0; xa<= 9; xa++) { ENCODE[xa+52]=(char)('0'+xa); } // 52..61 -> '0'..'9'
ENCODE[62]='+';
ENCODE[63]='/';
for(int xa=0; xa<256; xa++) { DECODE[xa]=IGNORE; } // set all chars to IGNORE, first
for(int xa=0; xa< 64; xa++) { DECODE[ENCODE[xa]]=xa; } // set the Base 64 chars to their integer byte values
DECODE['=']=PAD;
}
// *****************************************************************************
// STATIC METHODS
// *****************************************************************************
static public String toString(byte[] dta) {
return BASE64.encode(dta);
}
static public String toString(byte[] dta, int str, int len) {
return BASE64.encode(dta,str,len);
}
static public byte[] toBytes(String b64) {
return BASE64.decode(b64);
}
static public byte[] toBytes(String b64, int str, int len) {
return BASE64.decode(b64,str,len);
}
} // END PUBLIC CLASS