[New] Implemented RFC 4648 Base de/encoding
Both encoding and decoding has been implemented. The specialty of this implementation is that it is possible to pass in your own alphabet thus allowing an extension without a re-implementation (again). As an addition the dbase32 alphabet was added. See http://docs.novacut.com/dbase32/dbase32.html for usage details
This commit is contained in:
parent
f04de1e935
commit
5ddb277773
|
@ -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
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ch.eitchnet.utils.helper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This class implements the decoding part of RFC 4648 <a>https://tools.ietf.org/html/rfc4648</a>. For the encoding see
|
||||
* {@link BaseEncoding}
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* All versions are implemented: Base64 with URL and file name safe encoding, Base32 with HEX and Base16
|
||||
* </p>
|
||||
*
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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 <eitch@eitchnet.ch>
|
||||
*
|
||||
*/
|
||||
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));
|
||||
}
|
||||
}
|
|
@ -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 <eitch@eitchnet.ch>
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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 <eitch@eitchnet.ch>
|
||||
*/
|
||||
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<Byte, Byte> valueToIndex = new HashMap<Byte, Byte>();
|
||||
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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue