diff --git a/src/main/java/ch/eitchnet/utils/helper/BaseDecoding.java b/src/main/java/ch/eitchnet/utils/helper/BaseDecoding.java
new file mode 100644
index 000000000..412486a36
--- /dev/null
+++ b/src/main/java/ch/eitchnet/utils/helper/BaseDecoding.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2012, Robert von Burg
+ *
+ * All rights reserved.
+ *
+ * This file is part of the ch.eitchnet.java.utils.
+ *
+ * ch.eitchnet.java.utils is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * ch.eitchnet.java.utils is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ch.eitchnet.java.utils. If not, see
+ * .
+ */
+package ch.eitchnet.utils.helper;
+
+/**
+ *
+ * This class implements the decoding part of RFC 4648 https://tools.ietf.org/html/rfc4648. For the encoding see
+ * {@link BaseEncoding}
+ *
+ *
+ *
+ * All versions are implemented: Base64 with URL and file name safe encoding, Base32 with HEX and Base16
+ *
+ *
+ * @author Robert von Burg
+ */
+public class BaseDecoding {
+
+ // private static final Logger logger = LoggerFactory.getLogger(BaseDecoding.class);
+
+ private static final byte PAD = '=';
+
+ // these reverse base encoding alphabets were generated from the actual alphabet
+
+ private static final byte[] REV_BASE_16 = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+ private static final byte[] REV_BASE_32 = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+ private static final byte[] REV_BASE_32_CROCKFORD = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15,
+ 16, 17, -1, 18, 19, -1, 20, 21, -1, 22, 23, 24, 25, 26, -1, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1 };
+ private static final byte[] REV_BASE_32_DMEDIA = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, -1, -1, -1, -1, -1, -1, -1, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1 };
+ private static final byte[] REV_BASE_32_HEX = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1 };
+ private static final byte[] REV_BASE_64 = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1,
+ -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 };
+ private static final byte[] REV_BASE_64_SAFE = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 62, -1, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1,
+ -1, -1 };
+
+ public static byte[] fromBase64(byte[] bytes) {
+ return fromBase64(REV_BASE_64, bytes);
+ }
+
+ public static byte[] fromBase64Safe(byte[] bytes) {
+ return fromBase64(REV_BASE_64_SAFE, bytes);
+ }
+
+ public static byte[] fromBase32(byte[] bytes) {
+ return fromBase32(REV_BASE_32, bytes);
+ }
+
+ public static byte[] fromBase32Hex(byte[] bytes) {
+ return fromBase32(REV_BASE_32_HEX, bytes);
+ }
+
+ public static byte[] fromBase32Dmedia(byte[] bytes) {
+ return fromBase32(REV_BASE_32_DMEDIA, bytes);
+ }
+
+ public static byte[] fromBase32Crockford(byte[] bytes) {
+ return fromBase32(REV_BASE_32_CROCKFORD, bytes);
+ }
+
+ public static byte[] fromBase16(byte[] bytes) {
+ return fromBase16(REV_BASE_16, bytes);
+ }
+
+ /**
+ * Decodes the given Base64 encoded data to the original data set
+ *
+ * @param alphabet
+ * the 64-bit alphabet to use
+ * @param bytes
+ * the bytes to decode
+ *
+ * @return the decoded data
+ */
+ public static byte[] fromBase64(byte[] alphabet, byte[] bytes) {
+ int inputLength = bytes.length;
+ if (inputLength == 0)
+ return new byte[0];
+ if ((inputLength % 4) != 0) {
+ throw new RuntimeException("The input bytes to be decoded must be multiples of 4, but is multiple of "
+ + (inputLength % 4));
+ }
+
+ if (alphabet.length != 128)
+ throw new RuntimeException("Alphabet does not have expected size 128 but is " + alphabet.length);
+
+ // find how much padding we have
+ int nrOfBytesPadding = 0;
+ if (bytes[inputLength - 1] == PAD) {
+ int end = inputLength - 1;
+ while (bytes[end] == PAD)
+ end--;
+ if (end != inputLength - 1)
+ nrOfBytesPadding = inputLength - 1 - end;
+ }
+
+ int inputDataLength = inputLength - nrOfBytesPadding;
+ int dataLengthBits = inputDataLength * 6; // 6 bits data for every 8 bits inputs
+ // multiples of 6 required
+ // truncating is no problem due to the input having padding to have multiples of 32 bits
+ dataLengthBits = dataLengthBits - (dataLengthBits % 8);
+ int dataLengthBytes = dataLengthBits / 8;
+
+ // f => Zg==
+ // fo => Zm8=
+ // foo => Zm9v
+
+ // we want to write as much as 24 bits in multiples of 6.
+ // these multiples of 6 are read from multiples of 8
+ // i.e. we discard 2 bits from every 8 bits input
+ // thus we need to read 24 / 6 = 4 bytes
+
+ byte[] data = new byte[dataLengthBytes];
+ int dataPos = 0;
+
+ // but we simply ignore the padding
+ int bytesPos = 0;
+ while (bytesPos < inputDataLength) {
+ int remaining = inputDataLength - bytesPos;
+
+ long bits;
+ if (remaining >= 4) {
+
+ // XXX check each byte value so that it is legal
+
+ bits = ((long) (alphabet[bytes[bytesPos++]] & 63) << 18) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 63) << 12) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 63) << 6) //
+ | (alphabet[bytes[bytesPos++]] & 63);
+
+ } else if (remaining == 3) {
+
+ bits = ((long) (alphabet[bytes[bytesPos++]] & 63) << 18) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 63) << 12) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 63) << 6);
+
+ } else if (remaining == 2) {
+
+ bits = ((long) (alphabet[bytes[bytesPos++]] & 63) << 18) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 63) << 12);
+//
+// long b;
+// byte a;
+// a = bytes[0];
+// logger.info("1 a: " + a + " " + ((char) a) + " - " + ByteHelper.asBinary(a) + " " + indexOf(a));
+// b = (byte) (alphabet[a] & 63);
+// logger.info("1 b: " + b + " - " + ((char) b) + " - " + ByteHelper.asBinary(b));
+// a = bytes[1];
+// logger.info("2 a: " + a + " " + ((char) a) + " - " + ByteHelper.asBinary(a) + " " + indexOf(a));
+// b = (byte) (alphabet[a] & 63);
+// logger.info("2 b: " + b + " - " + ((char) b) + " - " + ByteHelper.asBinary(b));
+
+ } else if (remaining == 1) {
+
+ bits = ((alphabet[bytes[bytesPos++]] & 63) << 18);
+
+ } else {
+
+ bits = 0L;
+ }
+
+ // we can truncate to 8 bits
+ int toWrite = remaining >= 4 ? 3 : remaining * 6 / 8;
+ // max is always 3 bytes data from 4 bytes input
+
+// logger.info("toWrite: " + toWrite + ", remaining: " + remaining);
+// logger.info("bits: " + ByteHelper.asBinary(bits));
+
+ // always start at 24. bit
+ int bitPos = 23;
+ // always write 24 bits (8 bits * n bytes)
+ for (int i = 0; i < toWrite; i++) {
+
+ byte value = 0;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 128;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 64;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 32;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 16;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 8;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 4;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 2;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 1;
+ bitPos--;
+ data[dataPos] = value;
+ dataPos++;
+ }
+ }
+
+ return data;
+ }
+
+//
+// /**
+// * @param a
+// * @return
+// */
+// private static int indexOf(byte a) {
+// for (int i = 0; i < BaseEncoding.BASE_64.length; i++) {
+// if (BaseEncoding.BASE_64[i] == a)
+// return i;
+// }
+// return -1;
+// }
+//
+// private static int indexOf1(byte a) {
+// for (int i = 0; i < REV_BASE_64.length; i++) {
+// if (REV_BASE_64[i] == a)
+// return i;
+// }
+// return -1;
+// }
+
+ /**
+ * Decodes the given Base32 encoded data to the original data set
+ *
+ * @param alphabet
+ * the 32-bit alphabet to use
+ * @param bytes
+ * the bytes to decode
+ *
+ * @return the decoded data
+ */
+ public static byte[] fromBase32(byte[] alphabet, byte[] bytes) {
+ int inputLength = bytes.length;
+ if (inputLength == 0)
+ return new byte[0];
+ if ((inputLength % 8) != 0) {
+ throw new RuntimeException("The input bytes to be decoded must be multiples of 8, but is multiple of "
+ + (inputLength % 8));
+ }
+
+ if (alphabet.length != 128)
+ throw new RuntimeException("Alphabet does not have expected size 128 but is " + alphabet.length);
+
+ // find how much padding we have
+ int nrOfBytesPadding = 0;
+ if (bytes[inputLength - 1] == PAD) {
+ int end = inputLength - 1;
+ while (bytes[end] == PAD)
+ end--;
+ if (end != inputLength - 1)
+ nrOfBytesPadding = inputLength - 1 - end;
+ }
+
+ int inputDataLength = inputLength - nrOfBytesPadding;
+ int dataLengthBits = inputDataLength * 5; // 5 bits data for every 8 bits inputs
+ // multiples of 8 required
+ // truncating is no problem due to the input having padding to have multiples of 40 bits
+ dataLengthBits = dataLengthBits - (dataLengthBits % 8);
+ int dataLengthBytes = dataLengthBits / 8;
+
+// logger.info("Input " + inputLength + " bytes, InputData " + inputDataLength + " bytes, Padding: "
+// + nrOfBytesPadding + " bytes, dataLength: " + dataLengthBits + " bits, dataLengthBytes: "
+// + dataLengthBytes + " bytes");
+// logger.info(ByteHelper.asBinary(bytes));
+
+ // we want to write as much as 40 bits in multiples of 5.
+ // these multiples of 5 are read from multiples of 8
+ // i.e. we discard 3 bits from every 8 bits input
+ // thus we need to read 40 / 5 = 8 bytes
+
+ byte[] data = new byte[dataLengthBytes];
+ int dataPos = 0;
+
+ // but we simply ignore the padding
+ int bytesPos = 0;
+ while (bytesPos < inputDataLength) {
+ int remaining = inputDataLength - bytesPos;
+
+ long bits;
+ if (remaining >= 8) {
+
+ // XXX check each byte value so that it is legal
+
+ bits = ((long) (alphabet[bytes[bytesPos++]] & 31) << 35) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 30) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 25) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 20) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 15) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 10) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 5) //
+ | (alphabet[bytes[bytesPos++]] & 31);
+
+ } else if (remaining >= 7) {
+
+ bits = ((long) (alphabet[bytes[bytesPos++]] & 31) << 35) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 30) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 25) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 20) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 15) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 10) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 5);
+
+ } else if (remaining == 6) {
+
+ bits = ((long) (alphabet[bytes[bytesPos++]] & 31) << 35) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 30) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 25) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 20) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 15) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 10);
+
+ } else if (remaining == 5) {
+
+ bits = ((long) (alphabet[bytes[bytesPos++]] & 31) << 35) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 30) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 25) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 20) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 15);
+
+ } else if (remaining == 4) {
+
+ bits = ((long) (alphabet[bytes[bytesPos++]] & 31) << 35) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 30) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 25) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 20);
+
+ } else if (remaining == 3) {
+
+ bits = ((long) (alphabet[bytes[bytesPos++]] & 31) << 35) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 30) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 25);
+
+ } else if (remaining == 2) {
+
+ bits = ((long) (alphabet[bytes[bytesPos++]] & 31) << 35) //
+ | ((long) (alphabet[bytes[bytesPos++]] & 31) << 30);
+
+ } else if (remaining == 1) {
+
+ bits = ((alphabet[bytes[bytesPos++]] & 31) << 35);
+
+ } else {
+
+ bits = 0L;
+ }
+
+ // we can truncate to 8 bits
+ int toRead = remaining >= 8 ? 5 : remaining * 5 / 8;
+ // max is always 5 bytes data from 8 bytes input
+
+ // always start at 40. bit
+ int bitPos = 39;
+ // always write 40 bits (5 bytes * 8 bits)
+ for (int i = 0; i < toRead; i++) {
+
+ byte value = 0;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 128;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 64;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 32;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 16;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 8;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 4;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 2;
+ bitPos--;
+ value |= ((bits >>> bitPos) & 1) == 0 ? 0 : 1;
+ bitPos--;
+ data[dataPos] = value;
+ dataPos++;
+ }
+ }
+
+ return data;
+ }
+
+ /**
+ * Decodes the given Base16 encoded data to the original data set
+ *
+ * @param alphabet
+ * the 16-bit alphabet to use
+ * @param bytes
+ * the bytes to decode
+ *
+ * @return the decoded data
+ */
+ public static byte[] fromBase16(byte[] alphabet, byte[] bytes) {
+ if (bytes.length == 0)
+ return new byte[0];
+ if ((bytes.length % 2) != 0) {
+ throw new RuntimeException("The input bytes to be decoded must be multiples of 4, but is multiple of "
+ + (bytes.length % 4));
+ }
+
+ if (alphabet.length != 128)
+ throw new RuntimeException("Alphabet does not have expected size 128 but is " + alphabet.length);
+
+ int dataLength = bytes.length / 2;
+
+ byte[] data = new byte[dataLength];
+ for (int i = 0; i < bytes.length;) {
+
+ byte b1 = bytes[i++];
+ byte b2 = bytes[i++];
+
+ if (b1 < 0) {
+ throw new IllegalArgumentException("Value at index " + (i - 2) + " is not in range of alphabet (0-127)"
+ + b1);
+ }
+ if (b2 < 0) {
+ throw new IllegalArgumentException("Value at index " + (i - 1) + " is not in range of alphabet (0-127)"
+ + b2);
+ }
+
+ byte c1 = alphabet[b1];
+ byte c2 = alphabet[b2];
+
+ if (c1 == -1) {
+ throw new IllegalArgumentException("Value at index " + (i - 2)
+ + " is referencing illegal value in alphabet: " + b1);
+ }
+ if (c2 == -1) {
+ throw new IllegalArgumentException("Value at index " + (i - 2)
+ + " is referencing illegal value in alphabet: " + b2);
+ }
+
+ int dataIndex = (i / 2) - 1;
+ int value = ((c1 << 4) & 0xff) | c2;
+ data[dataIndex] = (byte) value;
+ }
+
+ return data;
+ }
+}
diff --git a/src/main/java/ch/eitchnet/utils/helper/BaseEncoding.java b/src/main/java/ch/eitchnet/utils/helper/BaseEncoding.java
index 8ed687540..36e070aa6 100644
--- a/src/main/java/ch/eitchnet/utils/helper/BaseEncoding.java
+++ b/src/main/java/ch/eitchnet/utils/helper/BaseEncoding.java
@@ -172,7 +172,7 @@ public class BaseEncoding {
// always start at 24. bit
int bitPos = 23;
- // always write 24 bits (6 bytes * 4 multiples), but this will also write into the padding
+ // always write 24 bits (6 bits * 4), but this will also write into the padding
// we will fix this by writing the padding as has been calculated previously
while (bitPos >= 0) {
diff --git a/src/main/java/ch/eitchnet/utils/helper/ByteHelper.java b/src/main/java/ch/eitchnet/utils/helper/ByteHelper.java
index c5d5483f8..9fc47cded 100644
--- a/src/main/java/ch/eitchnet/utils/helper/ByteHelper.java
+++ b/src/main/java/ch/eitchnet/utils/helper/ByteHelper.java
@@ -95,6 +95,25 @@ public class ByteHelper {
return sb.toString();
}
+ /**
+ * Formats the given byte array to a binary string, separating each byte by a space
+ *
+ * @param b
+ * the byte to format to a binary string
+ *
+ * @return the binary string
+ */
+ public static String asBinary(byte[] bytes) {
+ StringBuilder sb = new StringBuilder();
+
+ for (byte b : bytes) {
+ sb.append(asBinary(b));
+ sb.append(" ");
+ }
+
+ return sb.toString();
+ }
+
/**
* Formats the given integer to a binary string, each byte is separated by a space
*
diff --git a/src/test/java/ch/eitchnet/utils/helper/BaseDecodingTest.java b/src/test/java/ch/eitchnet/utils/helper/BaseDecodingTest.java
new file mode 100644
index 000000000..cebe427be
--- /dev/null
+++ b/src/test/java/ch/eitchnet/utils/helper/BaseDecodingTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2012, Robert von Burg
+ *
+ * All rights reserved.
+ *
+ * This file is part of the XXX.
+ *
+ * XXX is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * XXX is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XXX. If not, see
+ * .
+ */
+package ch.eitchnet.utils.helper;
+
+import static ch.eitchnet.utils.helper.BaseDecoding.fromBase16;
+import static ch.eitchnet.utils.helper.BaseDecoding.fromBase32;
+import static ch.eitchnet.utils.helper.BaseDecoding.fromBase32Hex;
+import static ch.eitchnet.utils.helper.BaseDecoding.fromBase64;
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Robert von Burg
+ *
+ */
+public class BaseDecodingTest {
+ private static final Logger logger = LoggerFactory.getLogger(BaseDecodingTest.class);
+
+ @Test
+ public void testBase64() {
+ Assert.assertEquals("", new String(fromBase64("".getBytes())));
+ Assert.assertEquals("f", new String(fromBase64("Zg==".getBytes())));
+ Assert.assertEquals("fo", new String(fromBase64("Zm8=".getBytes())));
+ Assert.assertEquals("foo", new String(fromBase64("Zm9v".getBytes())));
+ Assert.assertEquals("foob", new String(fromBase64("Zm9vYg==".getBytes())));
+ Assert.assertEquals("fooba", new String(fromBase64("Zm9vYmE=".getBytes())));
+ Assert.assertEquals("foobar", new String(fromBase64("Zm9vYmFy".getBytes())));
+
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = 'Z';
+ }
+ long start = System.nanoTime();
+ for (int i = 0; i < 200; i++) {
+ fromBase64(bytes);
+ }
+ long end = System.nanoTime();
+ logger.info("Decoding 200MB Base64 took " + StringHelper.formatNanoDuration(end - start));
+ }
+
+ @Test
+ public void testBase32() {
+ Assert.assertEquals("", new String(fromBase32("".getBytes())));
+ Assert.assertEquals("f", new String(fromBase32("MY======".getBytes())));
+ Assert.assertEquals("fo", new String(fromBase32("MZXQ====".getBytes())));
+ Assert.assertEquals("foo", new String(fromBase32("MZXW6===".getBytes())));
+ Assert.assertEquals("foob", new String(fromBase32("MZXW6YQ=".getBytes())));
+ Assert.assertEquals("fooba", new String(fromBase32("MZXW6YTB".getBytes())));
+ Assert.assertEquals("foobar", new String(fromBase32("MZXW6YTBOI======".getBytes())));
+
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = 'M';
+ }
+ long start = System.nanoTime();
+ for (int i = 0; i < 200; i++) {
+ fromBase32(bytes);
+ }
+ long end = System.nanoTime();
+ logger.info("Decoding 200MB Base32 took " + StringHelper.formatNanoDuration(end - start));
+ }
+
+ @Test
+ public void testBase32Hex() {
+ Assert.assertEquals("", new String(fromBase32Hex("".getBytes())));
+ Assert.assertEquals("f", new String(fromBase32Hex("CO======".getBytes())));
+ Assert.assertEquals("fo", new String(fromBase32Hex("CPNG====".getBytes())));
+ Assert.assertEquals("foo", new String(fromBase32Hex("CPNMU===".getBytes())));
+ Assert.assertEquals("foob", new String(fromBase32Hex("CPNMUOG=".getBytes())));
+ Assert.assertEquals("fooba", new String(fromBase32Hex("CPNMUOJ1".getBytes())));
+ Assert.assertEquals("foobar", new String(fromBase32Hex("CPNMUOJ1E8======".getBytes())));
+
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = 'C';
+ }
+ long start = System.nanoTime();
+ for (int i = 0; i < 200; i++) {
+ fromBase32Hex(bytes);
+ }
+ long end = System.nanoTime();
+ logger.info("Decoding 200MB Base32Hex took " + StringHelper.formatNanoDuration(end - start));
+ }
+
+ @Test
+ public void testBase16() {
+ Assert.assertEquals("", new String(fromBase16("".getBytes())));
+ Assert.assertEquals("f", new String(fromBase16("66".getBytes())));
+ Assert.assertEquals("fo", new String(fromBase16("666F".getBytes())));
+ Assert.assertEquals("foo", new String(fromBase16("666F6F".getBytes())));
+ Assert.assertEquals("foob", new String(fromBase16("666F6F62".getBytes())));
+ Assert.assertEquals("fooba", new String(fromBase16("666F6F6261".getBytes())));
+ Assert.assertEquals("foobar", new String(fromBase16("666F6F626172".getBytes())));
+
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = '6';
+ }
+ long start = System.nanoTime();
+ for (int i = 0; i < 200; i++) {
+ fromBase16(bytes);
+ }
+ long end = System.nanoTime();
+ logger.info("Decoding 200MB Base16 took " + StringHelper.formatNanoDuration(end - start));
+ }
+}
diff --git a/src/test/java/ch/eitchnet/utils/helper/BaseEncodingTest.java b/src/test/java/ch/eitchnet/utils/helper/BaseEncodingTest.java
index d6c85ef13..c30417014 100644
--- a/src/test/java/ch/eitchnet/utils/helper/BaseEncodingTest.java
+++ b/src/test/java/ch/eitchnet/utils/helper/BaseEncodingTest.java
@@ -28,6 +28,8 @@ import static ch.eitchnet.utils.helper.BaseEncoding.toBase64;
import junit.framework.Assert;
import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* @author Robert von Burg
@@ -35,7 +37,7 @@ import org.junit.Test;
*/
public class BaseEncodingTest {
- // private static final Logger logger = LoggerFactory.getLogger(BaseEncodingTest.class);
+ private static final Logger logger = LoggerFactory.getLogger(BaseEncodingTest.class);
@Test
public void testBase64() {
@@ -46,6 +48,14 @@ public class BaseEncodingTest {
Assert.assertEquals("Zm9vYg==", new String(toBase64("foob".getBytes())));
Assert.assertEquals("Zm9vYmE=", new String(toBase64("fooba".getBytes())));
Assert.assertEquals("Zm9vYmFy", new String(toBase64("foobar".getBytes())));
+
+ long start = System.nanoTime();
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < 200; i++) {
+ toBase64(bytes);
+ }
+ long end = System.nanoTime();
+ logger.info("Encoding 200MB Base64 took " + StringHelper.formatNanoDuration(end - start));
}
@Test
@@ -57,6 +67,14 @@ public class BaseEncodingTest {
Assert.assertEquals("MZXW6YQ=", new String(toBase32("foob".getBytes())));
Assert.assertEquals("MZXW6YTB", new String(toBase32("fooba".getBytes())));
Assert.assertEquals("MZXW6YTBOI======", new String(toBase32("foobar".getBytes())));
+
+ long start = System.nanoTime();
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < 200; i++) {
+ toBase32(bytes);
+ }
+ long end = System.nanoTime();
+ logger.info("Encoding 200MB Base32 took " + StringHelper.formatNanoDuration(end - start));
}
@Test
@@ -68,6 +86,14 @@ public class BaseEncodingTest {
Assert.assertEquals("CPNMUOG=", new String(toBase32Hex("foob".getBytes())));
Assert.assertEquals("CPNMUOJ1", new String(toBase32Hex("fooba".getBytes())));
Assert.assertEquals("CPNMUOJ1E8======", new String(toBase32Hex("foobar".getBytes())));
+
+ long start = System.nanoTime();
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < 200; i++) {
+ toBase32Hex(bytes);
+ }
+ long end = System.nanoTime();
+ logger.info("Encoding 200MB Base32Hex took " + StringHelper.formatNanoDuration(end - start));
}
@Test
@@ -79,5 +105,13 @@ public class BaseEncodingTest {
Assert.assertEquals("666F6F62", new String(toBase16("foob".getBytes())));
Assert.assertEquals("666F6F6261", new String(toBase16("fooba".getBytes())));
Assert.assertEquals("666F6F626172", new String(toBase16("foobar".getBytes())));
+
+ long start = System.nanoTime();
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < 200; i++) {
+ toBase16(bytes);
+ }
+ long end = System.nanoTime();
+ logger.info("Encoding 200MB Base16 took " + StringHelper.formatNanoDuration(end - start));
}
}
diff --git a/src/test/java/ch/eitchnet/utils/helper/GenerateReverseBaseEncodingAlphabets.java b/src/test/java/ch/eitchnet/utils/helper/GenerateReverseBaseEncodingAlphabets.java
new file mode 100644
index 000000000..3e22f4331
--- /dev/null
+++ b/src/test/java/ch/eitchnet/utils/helper/GenerateReverseBaseEncodingAlphabets.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2012, Robert von Burg
+ *
+ * All rights reserved.
+ *
+ * This file is part of the XXX.
+ *
+ * XXX is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * XXX is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XXX. If not, see
+ * .
+ */
+package ch.eitchnet.utils.helper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Simple helper class to generate the reverse alphabets for {@link BaseDecoding}
+ *
+ * @author Robert von Burg
+ */
+public class GenerateReverseBaseEncodingAlphabets {
+
+ public static void main(String[] args) {
+
+ System.out.println(generateReverseAlphabet("REV_BASE_16", BaseEncoding.BASE_16));
+ System.out.println(generateReverseAlphabet("REV_BASE_32", BaseEncoding.BASE_32));
+ System.out.println(generateReverseAlphabet("REV_BASE_32_CROCKFORD", BaseEncoding.BASE_32_CROCKFORD));
+ System.out.println(generateReverseAlphabet("REV_BASE_32_DMEDIA", BaseEncoding.BASE_32_DMEDIA));
+ System.out.println(generateReverseAlphabet("REV_BASE_32_HEX", BaseEncoding.BASE_32_HEX));
+ System.out.println(generateReverseAlphabet("REV_BASE_64", BaseEncoding.BASE_64));
+ System.out.println(generateReverseAlphabet("REV_BASE_64_SAFE", BaseEncoding.BASE_64_SAFE));
+ }
+
+ public static String generateReverseAlphabet(String name, byte[] alphabet) {
+
+ Map valueToIndex = new HashMap();
+ for (byte i = 0; i < alphabet.length; i++) {
+ Byte value = Byte.valueOf(i);
+ Byte key = Byte.valueOf(alphabet[value]);
+ if (valueToIndex.containsKey(key))
+ throw new RuntimeException("Alphabet hast twice the same value " + key + " at index " + value);
+ valueToIndex.put(key, value);
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("private static final byte[] " + name + " = { ");
+
+ Byte minusOne = Byte.valueOf((byte) -1);
+ for (int i = 0; i < 128; i++) {
+ Byte index = Byte.valueOf((byte) i);
+ Byte value = valueToIndex.get(index);
+ if (value == null)
+ sb.append(minusOne.toString());
+ else
+ sb.append(value.toString());
+
+ if (i < 127)
+ sb.append(", ");
+ }
+
+ sb.append(" };");
+
+ return sb.toString();
+ }
+}