package com.google_api;
/**
* Created by pritom on 20/11/2016.
*/
public class Base64Decoder {
private static final String ENCODING = "UTF-8";
private static final byte[] CHUNK_SEPARATOR = new byte[]{(byte)13, (byte)10};
private static final byte[] DECODE_TABLE = new byte[]{
(byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1,
(byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1,
(byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1,
(byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1,
(byte)-1, (byte)-1, (byte)-1, (byte)62, (byte)-1, (byte)62, (byte)-1, (byte)63, (byte)52, (byte)53,
(byte)54, (byte)55, (byte)56, (byte)57, (byte)58, (byte)59, (byte)60, (byte)61, (byte)-1, (byte)-1,
(byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)-1, (byte)0, (byte)1, (byte)2, (byte)3, (byte)4, (byte)5,
(byte)6, (byte)7, (byte)8, (byte)9, (byte)10, (byte)11, (byte)12, (byte)13, (byte)14, (byte)15, (byte)16,
(byte)17, (byte)18, (byte)19, (byte)20, (byte)21, (byte)22, (byte)23, (byte)24, (byte)25, (byte)-1,
(byte)-1, (byte)-1, (byte)-1, (byte)63, (byte)-1, (byte)26, (byte)27, (byte)28, (byte)29, (byte)30,
(byte)31, (byte)32, (byte)33, (byte)34, (byte)35, (byte)36, (byte)37, (byte)38, (byte)39, (byte)40,
(byte)41, (byte)42, (byte)43, (byte)44, (byte)45, (byte)46, (byte)47, (byte)48, (byte)49, (byte)50, (byte)51};
private final byte[] decodeTable;
private final int decodeSize;
private int bitWorkArea;
private byte[] buffer;
private int pos;
private int readPos;
private boolean eof;
private int modulus;
public static String decodeBase64(String raw) throws Exception {
return new String(new Base64Decoder(0, CHUNK_SEPARATOR).decode(raw), ENCODING);
}
private Base64Decoder(int lineLength, byte[] lineSeparator) throws Exception {
super();
this.decodeTable = DECODE_TABLE;
int encodeSize;
if(lineSeparator != null) {
if(this.containsAlphabetOrPad(lineSeparator)) {
String sep = newStringUtf8(lineSeparator);
throw new Exception("lineSeparator must not contain base64 characters: [" + sep + "]");
}
if(lineLength > 0) {
encodeSize = 4 + lineSeparator.length;
System.arraycopy(lineSeparator, 0, new byte[lineSeparator.length], 0, lineSeparator.length);
} else {
encodeSize = 4;
}
} else {
encodeSize = 4;
}
this.decodeSize = encodeSize - 1;
}
private void decode(byte[] in, int inPos, int inAvail) {
if(!this.eof) {
if(inAvail < 0) {
this.eof = true;
}
for(int i = 0; i < inAvail; ++i) {
this.ensureBufferSize(this.decodeSize);
byte b = in[inPos++];
if(b == 61) {
this.eof = true;
break;
}
if(b >= 0 && b < DECODE_TABLE.length) {
byte result = DECODE_TABLE[b];
if(result >= 0) {
this.modulus = (this.modulus + 1) % 4;
this.bitWorkArea = (this.bitWorkArea << 6) + result;
if(this.modulus == 0) {
this.buffer[this.pos++] = (byte)(this.bitWorkArea >> 16 & 255);
this.buffer[this.pos++] = (byte)(this.bitWorkArea >> 8 & 255);
this.buffer[this.pos++] = (byte)(this.bitWorkArea & 255);
}
}
}
}
if(this.eof && this.modulus != 0) {
this.ensureBufferSize(this.decodeSize);
switch(this.modulus) {
case 2:
this.bitWorkArea >>= 4;
this.buffer[this.pos++] = (byte)(this.bitWorkArea & 255);
break;
case 3:
this.bitWorkArea >>= 2;
this.buffer[this.pos++] = (byte)(this.bitWorkArea >> 8 & 255);
this.buffer[this.pos++] = (byte)(this.bitWorkArea & 255);
}
}
}
}
private boolean isInAlphabet(byte octet) {
return octet >= 0 && octet < this.decodeTable.length && this.decodeTable[octet] != -1;
}
private int available() {
return this.buffer != null ? this.pos - this.readPos : 0;
}
private int getDefaultBufferSize() {
return 8192;
}
private void resizeBuffer() {
if(this.buffer == null) {
this.buffer = new byte[this.getDefaultBufferSize()];
this.pos = 0;
this.readPos = 0;
}
else {
byte[] b = new byte[this.buffer.length * 2];
System.arraycopy(this.buffer, 0, b, 0, this.buffer.length);
this.buffer = b;
}
}
private void ensureBufferSize(int size) {
if(this.buffer == null || this.buffer.length < this.pos + size) {
this.resizeBuffer();
}
}
private int readResults(byte[] b, int bPos, int bAvail) {
if(this.buffer != null) {
int len = Math.min(this.available(), bAvail);
System.arraycopy(this.buffer, this.readPos, b, bPos, len);
this.readPos += len;
if(this.readPos >= this.pos) {
this.buffer = null;
}
return len;
}
else {
return this.eof ? -1 : 0;
}
}
private void reset() {
this.buffer = null;
this.pos = 0;
this.readPos = 0;
this.modulus = 0;
this.eof = false;
}
private byte[] decode(String pArray) throws Exception {
return this.decode(getBytesUtf8(pArray));
}
private byte[] decode(byte[] pArray) {
this.reset();
if(pArray != null && pArray.length != 0) {
this.decode(pArray, 0, pArray.length);
this.decode(pArray, 0, -1);
byte[] result = new byte[this.pos];
this.readResults(result, 0, result.length);
return result;
} else {
return pArray;
}
}
private boolean containsAlphabetOrPad(byte[] arrayOctet) {
if(arrayOctet == null) {
return false;
}
else {
for (byte element : arrayOctet) {
if (61 == element || this.isInAlphabet(element)) {
return true;
}
}
return false;
}
}
private static byte[] getBytesUtf8(String string) throws Exception {
return getBytesUnchecked(string, "UTF-8");
}
private static byte[] getBytesUnchecked(String string, String charsetName) throws Exception {
return string.getBytes(charsetName);
}
private static String newString(byte[] bytes, String charsetName) throws Exception {
return new String(bytes, charsetName);
}
private static String newStringUtf8(byte[] bytes) throws Exception {
return newString(bytes, "UTF-8");
}
}