// 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); } }