package net.varkhan.base.conversion.character; import net.varkhan.base.conversion.AbstractDecoder; import net.varkhan.base.conversion.Decoder; import net.varkhan.base.conversion.serializer.DecodingException; import java.io.IOException; import java.io.InputStream; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; /** * <b></b>. * <p/> * * @author varkhan * @date 11/23/13 * @time 2:54 PM */ public class Base64Decoder<C> extends AbstractDecoder<String,C> implements Decoder<String,C> { protected static final char[] CHARMAP_UTF7= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); protected static final char[] CHARMAP_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray(); protected static final char[] CHARMAP_XML = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-".toCharArray(); public static final char NO_PADDING = '\u0000'; public static final Base64Decoder<?> UTF7 = new Base64Decoder<Object>(CHARMAP_UTF7); public static final Base64Decoder<?> URL = new Base64Decoder<Object>(CHARMAP_URL, NO_PADDING); public static final Base64Decoder<?> XML = new Base64Decoder<Object>(CHARMAP_XML, NO_PADDING); protected final char[] map; protected final char pad; public Base64Decoder() { this(CHARMAP_UTF7, '='); } public Base64Decoder(char[] map) { this(map, '='); } public Base64Decoder(char[] map, char pad) { if(map.length!=64) throw new IllegalArgumentException("Character map should have exactly 64 characters"); this.map=map; this.pad=pad; } public char[] getCharacterMap() { return map.clone(); } public String decode(InputStream stm, C ctx) { try { return _decode(new StringBuilder(), map, pad, stm).toString(); } catch(IOException e) { /* Never happens -- return null to make compiler happy*/ return null; } } public String decode(ByteBuffer buf, C ctx) { try { return _decode(new StringBuilder(), map, pad, buf).toString(); } catch(IOException e) { /* Never happens -- return null to make compiler happy*/ return null; } } public String decode(byte[] dat, long pos, long len, C ctx) { try { return _decode(new StringBuilder(), map, pad, dat, pos, len).toString(); } catch(IOException e) { /* Never happens -- return null to make compiler happy*/ return null; } } public static <A extends Appendable> A _decode(A out, final char[] map, char pad, InputStream stm) throws IOException { try { int r=stm.read(); int i=0; int b0=0, b1=0, b2=0; while(r>=0) { switch(i%3) { case 0: // ****** **.... ...... ...... b0 = 0xFF&r; out.append(map[(0xF7&b0)>>2]); break; case 1: // ______ ==**** ****.. ...... b1 = 0xFF&r; out.append(map[((0x03&b0)<<4)|((0xF0&b1)>>>4)]); break; case 2: // ______ ______ ====** ****** b2 = 0xFF&r; out.append(map[((0x0F&b1)<<2)|((0xC0&b2)>>>6)]); out.append(map[0x3F&b2]); b0=b1=b2=0; break; } i++; r=stm.read(); } switch(i%3) { case 0: // ______ ______ ______ ______ // Nothing to do here break; case 1: // ______ ==.... ...... ...... out.append(map[(0x03&b0)<<4]); if(pad!=NO_PADDING) out.append(pad).append(pad); break; case 2: // ______ ______ ====.. ...... out.append(map[(0x0F&b1)<<2]); if(pad!=NO_PADDING) out.append(pad); break; } } catch(IOException e) { throw new DecodingException(e); } return out; } public static <A extends Appendable> A _decode(A out, final char[] map, char pad, ByteBuffer buf) throws IOException { try { int i=0; int b0=0, b1=0, b2=0; while(buf.position()<buf.limit()) { switch(i%3) { case 0: // ****** **.... ...... ...... b0 = 0xFF&buf.get(); out.append(map[(0xF7&b0)>>2]); break; case 1: // ______ ==**** ****.. ...... b1 = 0xFF&buf.get(); out.append(map[((0x03&b0)<<4)|((0xF0&b1)>>>4)]); break; case 2: // ______ ______ ====** ****** b2 = 0xFF&buf.get(); out.append(map[((0x0F&b1)<<2)|((0xC0&b2)>>>6)]); out.append(map[0x3F&b2]); b0=b1=b2=0; break; } i++; } switch(i%3) { case 0: // ______ ______ ______ ______ // Nothing to do here break; case 1: // ______ ==.... ...... ...... out.append(map[(0x03&b0)<<4]); if(pad!=NO_PADDING) out.append(pad).append(pad); break; case 2: // ______ ______ ====.. ...... out.append(map[(0x0F&b1)<<2]); if(pad!=NO_PADDING) out.append(pad); break; } } catch(BufferOverflowException e) { throw new DecodingException(e); } catch(ReadOnlyBufferException e) { throw new DecodingException(e); } return out; } public static <A extends Appendable> A _decode(A out, final char[] map, char pad, byte[] dat, long pos, long len) throws IOException { try { int p = (int)pos; int i=0; int b0=0, b1=0, b2=0; while(p<len) { switch(i%3) { case 0: // ****** **.... ...... ...... b0 = 0xFF&dat[p++]; out.append(map[(0xF7&b0)>>2]); break; case 1: // ______ ==**** ****.. ...... b1 = 0xFF&dat[p++]; out.append(map[((0x03&b0)<<4)|((0xF0&b1)>>>4)]); break; case 2: // ______ ______ ====** ****** b2 = 0xFF&dat[p++]; out.append(map[((0x0F&b1)<<2)|((0xC0&b2)>>>6)]); out.append(map[0x3F&b2]); b0=b1=b2=0; break; } i++; } switch(i%3) { case 0: // ______ ______ ______ ______ // Nothing to do here break; case 1: // ______ ==.... ...... ...... out.append(map[(0x03&b0)<<4]); if(pad!=NO_PADDING) out.append(pad).append(pad); break; case 2: // ______ ______ ====.. ...... out.append(map[(0x0F&b1)<<2]); if(pad!=NO_PADDING) out.append(pad); break; } } catch(ArrayIndexOutOfBoundsException e) { throw new DecodingException(e); } return out; } }