// Copyright (c) 2001 Dustin Sallings <dustin@spy.net>
package net.spy.util;
/**
* Base64 block encoder/decoder.
*/
public class Base64 extends Object {
private static final char[] CHARMAP={
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
private static Base64 instance=null;
/**
* Get a base64 encode/decoder.
*/
public Base64() {
super();
}
/**
* Get the singleton base64 instance.
*/
public static Base64 getInstance() {
if(instance == null) {
instance=new Base64();
}
return instance;
}
/**
* Encode some bytes to a base64 string.
*/
public String encode(byte data[]) {
// Go ahead and guess how big it needs to be.
StringBuilder sb=new StringBuilder((data.length*4/3));
int o=0;
// Flip through the input and encode the shite.
for(int i=0; i<data.length; i+=3) {
int a, b, c, tmpa, tmpb;
a=(data[i] & 0xff);
sb.append(CHARMAP[(a>>2)]);
tmpa=((a&0x03)<<4);
// If there's another byte, grab it and process it
if(data.length > i+1) {
b=(data[i+1] & 0xff);
tmpb=(b>>4);
sb.append(CHARMAP[(tmpa|tmpb)]);
tmpa=((b&0x0f)<<2);
if(data.length>i+2) {
c=(data[i+2] & 0xff);
tmpb=((c&0xc0)>>6);
sb.append(CHARMAP[(tmpa|tmpb)]);
sb.append(CHARMAP[(c&0x3f)]);
} else {
sb.append(CHARMAP[tmpa]);
sb.append('=');
}
} else {
// Only one byte in this block.
sb.append(CHARMAP[tmpa]);
sb.append('=');
sb.append('=');
}
o+=4;
if( (o%76)==0) {
sb.append("\r\n");
}
}
return(sb.toString());
}
/**
* Decode a string back into the bytes.
*/
public byte[] decode(String input) {
// Get enough room to store the output.
int size=(input.length()*3/4);
int insize=input.length();
if(input.endsWith("=")) {
size--;
insize--;
if(input.endsWith("==")) {
size--;
insize--;
}
}
byte[] rv=new byte[size];
int pos=0;
int count=0;
int packer=0;
int invalid=0;
for(int i=0; i<insize; i++) {
int x=mapIndex(input.charAt(i));
// Skip over invalid characters.
if(x<0) {
invalid++;
continue;
}
// Count valid chars.
count++;
// Pack them.
packer = packer << 6 | x;
// Every four bytes, we've got three valid output bytes.
if(count==4) {
rv[pos++]=(byte)((packer >> 16)&0xFF);
rv[pos++]=(byte)((packer >> 8)&0xFF);
rv[pos++]=(byte)(packer&0xFF);
count=0; packer=0;
}
}
// Any remainders?
if(count==2) {
rv[pos++]=(byte)(( (packer << 12) >> 16)&0xFF);
} else if(count==3) {
rv[pos++]=(byte)(( (packer << 6) >> 16)&0xFF);
rv[pos++]=(byte)(( (packer << 6) >> 8) & 0xFF);
}
// If there were any invalid characters, our size was wrong.
if(invalid>0) {
byte[] tmp=new byte[pos];
System.arraycopy(rv, 0, tmp, 0, pos);
rv=tmp;
}
return(rv);
}
/**
* Is this character a valid Base64 character?
*
* @return true if this character is in our Base64 character map.
*/
public boolean isValidBase64Char(char c) {
return(mapIndex(c)>=0);
}
private int mapIndex(char c) {
int rv=-1;
for(int i=0; i<CHARMAP.length && rv==-1; i++) {
if(CHARMAP[i]==c) {
rv=i;
}
}
return(rv);
}
}