Skip to content

Commit 86db746

Browse files
committed
Added Base32Hex implementation, and enabled the basic tests
Many more tests needed git-svn-id: https://svn.apache.org/repos/asf/commons/proper/codec/trunk@1063670 13f79535-47bb-0310-9956-ffa450edef68
1 parent 03d0f6c commit 86db746

2 files changed

Lines changed: 165 additions & 11 deletions

File tree

src/java/org/apache/commons/codec/binary/Base32.java

Lines changed: 164 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,22 @@ public class Base32 implements BinaryEncoder, BinaryDecoder {
101101
* This array is a lookup table that translates 5-bit positive integer index values into their "Base32 Alphabet"
102102
* equivalents as specified in Table 3 of RFC 2045.
103103
*/
104-
private static final byte[] STANDARD_ENCODE_TABLE = {
104+
private static final byte[] BASE32_ENCODE_TABLE = {
105105
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
106106
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
107107
'2', '3', '4', '5', '6', '7',
108108
};
109109

110+
/**
111+
* This array is a lookup table that translates 5-bit positive integer index values into their "Base32 Hex Alphabet"
112+
* equivalents as specified in Table 3 of RFC 2045.
113+
*/
114+
private static final byte[] BASE32HEX_ENCODE_TABLE = {
115+
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
116+
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
117+
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
118+
};
119+
110120
/**
111121
* Byte used to pad output.
112122
*/
@@ -118,7 +128,7 @@ public class Base32 implements BinaryEncoder, BinaryDecoder {
118128
* alphabet but fall within the bounds of the array are translated to -1.
119129
*
120130
*/
121-
private static final byte[] DECODE_TABLE = {
131+
private static final byte[] BASE32_DECODE_TABLE = {
122132
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
123133
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f
124134
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f
@@ -128,6 +138,22 @@ public class Base32 implements BinaryEncoder, BinaryDecoder {
128138
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 50-5a O-Z
129139
};
130140

141+
/**
142+
* This array is a lookup table that translates Unicode characters drawn from the "Base32 |Hex Alphabet" (as specified in
143+
* Table 3 of RFC 2045) into their 5-bit positive integer equivalents. Characters that are not in the Base32 Hex
144+
* alphabet but fall within the bounds of the array are translated to -1.
145+
*
146+
*/
147+
private static final byte[] BASE32HEX_DECODE_TABLE = {
148+
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
149+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f
150+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f
151+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 63, // 20-2f
152+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 30-3f 2-7
153+
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 40-4f A-N
154+
25, 26, 27, 28, 29, 30, 31, 32, // 50-57 O-V
155+
};
156+
131157
/** Mask used to extract 5 bits, used when encoding Base32 bytes */
132158
private static final int MASK_5BITS = 0x1f;
133159

@@ -142,6 +168,8 @@ public class Base32 implements BinaryEncoder, BinaryDecoder {
142168
* Encode table to use.
143169
*/
144170
private final byte[] encodeTable;
171+
172+
private final byte[] decodeTable; // need different decode table to support Hex version
145173

146174
/**
147175
* Line length for encoding. Not used when decoding. A value of zero or less implies no chunking of the Base32
@@ -215,7 +243,18 @@ public class Base32 implements BinaryEncoder, BinaryDecoder {
215243
*
216244
*/
217245
public Base32() {
218-
this(0);
246+
this(false);
247+
}
248+
249+
/**
250+
* Creates a Base32 codec used for decoding and encoding.
251+
* <p>
252+
* When encoding the line length is 0 (no chunking).
253+
* </p>
254+
* @param useHex if <code>true</code> then use Base32 Hex alphabet
255+
*/
256+
public Base32(boolean useHex) {
257+
this(0, null, useHex);
219258
}
220259

221260
/**
@@ -250,6 +289,28 @@ public Base32(int lineLength) {
250289
* The provided lineSeparator included some Base32 characters. That's not going to work!
251290
*/
252291
public Base32(int lineLength, byte[] lineSeparator) {
292+
this(lineLength, lineSeparator, false);
293+
}
294+
295+
/**
296+
* Creates a Base32 / Base32 Hex codec used for decoding and encoding.
297+
* <p>
298+
* When encoding the line length and line separator are given in the constructor.
299+
* </p>
300+
* <p>
301+
* Line lengths that aren't multiples of 8 will still essentially end up being multiples of 8 in the encoded data.
302+
* </p>
303+
*
304+
* @param lineLength
305+
* Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 8).
306+
* If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when decoding.
307+
* @param lineSeparator
308+
* Each line of encoded data will end with this sequence of bytes.
309+
* @param useHex if <code>true</code>, then use Base32 Hex alphabet, otherwise use Base32 alphabet
310+
* @throws IllegalArgumentException
311+
* The provided lineSeparator included some Base32 characters. That's not going to work!
312+
*/
313+
public Base32(int lineLength, byte[] lineSeparator, boolean useHex) {
253314
if (lineSeparator == null) {
254315
lineLength = 0; // disable chunk-separating
255316
lineSeparator = CHUNK_SEPARATOR; // this just gets ignored
@@ -267,7 +328,13 @@ public Base32(int lineLength, byte[] lineSeparator) {
267328
String sep = StringUtils.newStringUtf8(lineSeparator);
268329
throw new IllegalArgumentException("lineSeperator must not contain Base32 characters: [" + sep + "]");
269330
}
270-
this.encodeTable = STANDARD_ENCODE_TABLE; // TODO - encodeTable could perhaps be removed, but might be useful if merging with Base64
331+
if (useHex){
332+
this.encodeTable = BASE32HEX_ENCODE_TABLE;
333+
this.decodeTable = BASE32HEX_DECODE_TABLE;
334+
} else {
335+
this.encodeTable = BASE32_ENCODE_TABLE;
336+
this.decodeTable = BASE32_DECODE_TABLE;
337+
}
271338
}
272339

273340
/**
@@ -470,8 +537,8 @@ void decode(byte[] in, int inPos, int inAvail) { // package protected for access
470537
eof = true;
471538
break;
472539
} else {
473-
if (b >= 0 && b < DECODE_TABLE.length) {
474-
int result = DECODE_TABLE[b];
540+
if (b >= 0 && b < this.decodeTable.length) {
541+
int result = this.decodeTable[b];
475542
if (result >= 0) {
476543
modulus = (++modulus) % BYTES_PER_ENCODED_BLOCK;
477544
x = (x << BITS_PER_ENCODED_CHAR) + result; // collect decoded bytes
@@ -539,7 +606,18 @@ void decode(byte[] in, int inPos, int inAvail) { // package protected for access
539606
* @return <code>true</code> if the value is defined in the the Base32 alphabet (or pad), <code>false</code> otherwise.
540607
*/
541608
public static boolean isBase32(byte octet) {
542-
return octet == PAD || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1);
609+
return octet == PAD || (octet >= 0 && octet < BASE32_DECODE_TABLE.length && BASE32_DECODE_TABLE[octet] != -1);
610+
}
611+
612+
/**
613+
* Returns whether or not the <code>octet</code> is in the Base32 Hex alphabet.
614+
*
615+
* @param octet
616+
* The value to test
617+
* @return <code>true</code> if the value is defined in the the Base32 Hex alphabet (or pad), <code>false</code> otherwise.
618+
*/
619+
public static boolean isBase32Hex(byte octet) {
620+
return octet == PAD || (octet >= 0 && octet < BASE32HEX_DECODE_TABLE.length && BASE32HEX_DECODE_TABLE[octet] != -1);
543621
}
544622

545623
/**
@@ -612,6 +690,17 @@ public static String encodeBase32String(byte[] binaryData) {
612690
return StringUtils.newStringUtf8(encodeBase32(binaryData, false));
613691
}
614692

693+
/**
694+
* Encodes binary data using the Base32 algorithm but does not chunk the output.
695+
*
696+
* @param binaryData
697+
* binary data to encode
698+
* @return String containing Base32Hex characters.
699+
*/
700+
public static String encodeBase32HexString(byte[] binaryData) {
701+
return StringUtils.newStringUtf8(encodeBase32Hex(binaryData, false));
702+
}
703+
615704
/**
616705
* Encodes binary data using the Base32 algorithm and chunks the encoded output into 76 character blocks
617706
*
@@ -649,7 +738,6 @@ public Object decode(Object pObject) throws DecoderException {
649738
* @param pArray
650739
* A String containing Base32 character data
651740
* @return a byte array containing binary data
652-
* @since 1.4
653741
*/
654742
public byte[] decode(String pArray) {
655743
return decode(StringUtils.getBytesUtf8(pArray));
@@ -689,6 +777,21 @@ public static byte[] encodeBase32(byte[] binaryData, boolean isChunked) {
689777
return encodeBase32(binaryData, isChunked, Integer.MAX_VALUE);
690778
}
691779

780+
/**
781+
* Encodes binary data using the Base32 Hex algorithm, optionally chunking the output into 76 character blocks.
782+
*
783+
* @param binaryData
784+
* Array containing binary data to encode.
785+
* @param isChunked
786+
* if <code>true</code> this encoder will chunk the Base32 output into 76 character blocks
787+
* @return Base32Hex-encoded data.
788+
* @throws IllegalArgumentException
789+
* Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
790+
*/
791+
public static byte[] encodeBase32Hex(byte[] binaryData, boolean isChunked) {
792+
return encodeBase32Hex(binaryData, isChunked, Integer.MAX_VALUE);
793+
}
794+
692795
/**
693796
* Encodes binary data using the Base32 algorithm, optionally chunking the output into 76 character blocks.
694797
*
@@ -701,7 +804,6 @@ public static byte[] encodeBase32(byte[] binaryData, boolean isChunked) {
701804
* @return Base32-encoded data.
702805
* @throws IllegalArgumentException
703806
* Thrown when the input array needs an output array bigger than maxResultSize
704-
* @since 1.4
705807
*/
706808
public static byte[] encodeBase32(byte[] binaryData, boolean isChunked, int maxResultSize) {
707809
if (binaryData == null || binaryData.length == 0) {
@@ -716,7 +818,37 @@ public static byte[] encodeBase32(byte[] binaryData, boolean isChunked, int maxR
716818
maxResultSize);
717819
}
718820

719-
Base32 b64 = isChunked ? new Base32(MIME_CHUNK_SIZE, CHUNK_SEPARATOR) : new Base32(0, CHUNK_SEPARATOR);
821+
Base32 b64 = isChunked ? new Base32(MIME_CHUNK_SIZE, CHUNK_SEPARATOR) : new Base32();
822+
return b64.encode(binaryData);
823+
}
824+
825+
/**
826+
* Encodes binary data using the Base32Hex algorithm, optionally chunking the output into 76 character blocks.
827+
*
828+
* @param binaryData
829+
* Array containing binary data to encode.
830+
* @param isChunked
831+
* if <code>true</code> this encoder will chunk the Base32 output into 76 character blocks
832+
* @param maxResultSize
833+
* The maximum result size to accept.
834+
* @return Base32Hex-encoded data.
835+
* @throws IllegalArgumentException
836+
* Thrown when the input array needs an output array bigger than maxResultSize
837+
*/
838+
public static byte[] encodeBase32Hex(byte[] binaryData, boolean isChunked, int maxResultSize) {
839+
if (binaryData == null || binaryData.length == 0) {
840+
return binaryData;
841+
}
842+
843+
long len = getEncodeLength(binaryData, MIME_CHUNK_SIZE, CHUNK_SEPARATOR);
844+
if (len > maxResultSize) {
845+
throw new IllegalArgumentException("Input array too big, the output array would be bigger (" +
846+
len +
847+
") than the specified maxium size of " +
848+
maxResultSize);
849+
}
850+
851+
Base32 b64 = isChunked ? new Base32(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, true) : new Base32(true);
720852
return b64.encode(binaryData);
721853
}
722854

@@ -742,6 +874,28 @@ public static byte[] decodeBase32(byte[] base32Data) {
742874
return new Base32().decode(base32Data);
743875
}
744876

877+
/**
878+
* Decodes a Base32 Hex String into octets
879+
*
880+
* @param base32HexString
881+
* String containing Base32Hex data
882+
* @return Array containing decoded data.
883+
*/
884+
public static byte[] decodeBase32Hex(String base32HexString) {
885+
return new Base32(true).decode(base32HexString);
886+
}
887+
888+
/**
889+
* Decodes Base32 Hex data into octets
890+
*
891+
* @param base32HexData
892+
* Byte array containing Base32Hex data
893+
* @return Array containing decoded data.
894+
*/
895+
public static byte[] decodeBase32Hex(byte[] base32HexData) {
896+
return new Base32(true).decode(base32HexData);
897+
}
898+
745899
/**
746900
* Checks if a byte value is whitespace or not.
747901
*

src/test/org/apache/commons/codec/binary/Base32Test.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public void testBase32Samples() throws Exception {
5151

5252
public void testBase32HexSamples() throws Exception {
5353
for (int i = 0; i < BASE32HEX_TEST_CASES.length; i++) {
54-
// assertEquals(BASE32HEX_TEST_CASES[i][1], Base32.encodeBase32HexString(BASE32HEX_TEST_CASES[i][0].getBytes("UTF-8")));
54+
assertEquals(BASE32HEX_TEST_CASES[i][1], Base32.encodeBase32HexString(BASE32HEX_TEST_CASES[i][0].getBytes("UTF-8")));
5555
}
5656
}
5757

0 commit comments

Comments
 (0)