// ======================================================================== // $Id: B64Code.java,v 1.8 2005/03/15 10:04:25 gregwilkins Exp $ // Copyright 1999-2004 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== package net.lightbody.bmp.proxy.jetty.util; import java.io.UnsupportedEncodingException; /* ------------------------------------------------------------ */ /** Fast B64 Encoder/Decoder as described in RFC 1421. * <p>Does not insert or interpret whitespace as described in RFC * 1521. If you require this you must pre/post process your data. * <p> Note that in a web context the usual case is to not want * linebreaks or other white space in the encoded output. * * @version $Revision: 1.8 $ * @author Brett Sealey (bretts) * @author Greg Wilkins (gregw) */ public class B64Code { // ------------------------------------------------------------------ static final char pad='='; static final char[] nibble2code= { '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','+','/' }; static byte[] code2nibble=null; static { code2nibble=new byte[256]; for (int i=0;i<256;i++) code2nibble[i]=-1; for (byte b=0;b<64;b++) code2nibble[(byte)nibble2code[b]]=b; code2nibble[(byte)pad]=0; } // ------------------------------------------------------------------ /** * Base 64 encode as described in RFC 1421. * <p>Does not insert whitespace as described in RFC 1521. * @param s String to encode. * @return String containing the encoded form of the input. */ static public String encode(String s) { try { return encode(s,null); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException(e.toString()); } } // ------------------------------------------------------------------ /** * Base 64 encode as described in RFC 1421. * <p>Does not insert whitespace as described in RFC 1521. * @param s String to encode. * @param charEncoding String representing the name of * the character encoding of the provided input String. * @return String containing the encoded form of the input. */ static public String encode(String s,String charEncoding) throws UnsupportedEncodingException { byte[] bytes; if (charEncoding==null) bytes=s.getBytes(StringUtil.__ISO_8859_1); else bytes=s.getBytes(charEncoding); return new String(encode(bytes)); } // ------------------------------------------------------------------ /** * Fast Base 64 encode as described in RFC 1421. * <p>Does not insert whitespace as described in RFC 1521. * <p> Avoids creating extra copies of the input/output. * @param b byte array to encode. * @return char array containing the encoded form of the input. */ static public char[] encode(byte[] b) { if (b==null) return null; int bLen=b.length; char r[]=new char[((bLen+2)/3)*4]; int ri=0; int bi=0; byte b0, b1, b2; int stop=(bLen/3)*3; while (bi<stop) { b0=b[bi++]; b1=b[bi++]; b2=b[bi++]; r[ri++]=nibble2code[(b0>>>2)&0x3f]; r[ri++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f]; r[ri++]=nibble2code[(b1<<2)&0x3f|(b2>>>6)&0x03]; r[ri++]=nibble2code[b2&077]; } if (bLen!=bi) { switch (bLen%3) { case 2: b0=b[bi++]; b1=b[bi++]; r[ri++]=nibble2code[(b0>>>2)&0x3f]; r[ri++]=nibble2code[(b0<<4)&0x3f|(b1>>>4)&0x0f]; r[ri++]=nibble2code[(b1<<2)&0x3f]; r[ri++]=pad; break; case 1: b0=b[bi++]; r[ri++]=nibble2code[(b0>>>2)&0x3f]; r[ri++]=nibble2code[(b0<<4)&0x3f]; r[ri++]=pad; r[ri++]=pad; break; default: break; } } return r; } // ------------------------------------------------------------------ /** * Base 64 decode as described in RFC 1421. * <p>Does not attempt to cope with extra whitespace * as described in RFC 1521. * @param s String to decode * @return String decoded byte array. */ static public String decode(String s) { try { return decode(s,StringUtil.__ISO_8859_1); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException(e.toString()); } } // ------------------------------------------------------------------ /** * Base 64 decode as described in RFC 1421. * <p>Does not attempt to cope with extra whitespace * as described in RFC 1521. * @param s String to decode * @param charEncoding String representing the character encoding * used to map the decoded bytes into a String. * @return String decoded byte array. */ static public String decode(String s,String charEncoding) throws UnsupportedEncodingException { byte[] decoded=decode(s.toCharArray()); if (charEncoding==null) return new String(decoded); return new String(decoded,charEncoding); } /* ------------------------------------------------------------ */ /** * Fast Base 64 decode as described in RFC 1421. * <p>Does not attempt to cope with extra whitespace * as described in RFC 1521. * <p> Avoids creating extra copies of the input/output. * <p> Note this code has been flattened for performance. * @param b char array to decode. * @return byte array containing the decoded form of the input. * @throws IllegalArgumentException if the input is not a valid * B64 encoding. */ static public byte[] decode(char[] b) { if (b==null) return null; int bLen=b.length; if (bLen%4!=0) throw new IllegalArgumentException("Input block size is not 4"); int li=bLen-1; while (li>=0 && b[li]==(byte)pad) li--; if (li<0) return new byte[0]; // Create result array of exact required size. int rLen=((li+1)*3)/4; byte r[]=new byte[rLen]; int ri=0; int bi=0; int stop=(rLen/3)*3; byte b0,b1,b2,b3; try { while (ri<stop) { b0=code2nibble[b[bi++]]; b1=code2nibble[b[bi++]]; b2=code2nibble[b[bi++]]; b3=code2nibble[b[bi++]]; if (b0<0 || b1<0 || b2<0 || b3<0) throw new IllegalArgumentException("Not B64 encoded"); r[ri++]=(byte)(b0<<2|b1>>>4); r[ri++]=(byte)(b1<<4|b2>>>2); r[ri++]=(byte)(b2<<6|b3); } if (rLen!=ri) { switch (rLen%3) { case 2: b0=code2nibble[b[bi++]]; b1=code2nibble[b[bi++]]; b2=code2nibble[b[bi++]]; if (b0<0 || b1<0 || b2<0) throw new IllegalArgumentException("Not B64 encoded"); r[ri++]=(byte)(b0<<2|b1>>>4); r[ri++]=(byte)(b1<<4|b2>>>2); break; case 1: b0=code2nibble[b[bi++]]; b1=code2nibble[b[bi++]]; if (b0<0 || b1<0) throw new IllegalArgumentException("Not B64 encoded"); r[ri++]=(byte)(b0<<2|b1>>>4); break; default: break; } } } catch (IndexOutOfBoundsException e) { throw new IllegalArgumentException("char "+bi +" was not B64 encoded"); } return r; } }