@@ -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 *
0 commit comments