View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.codec.binary;
19  
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNull;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  
27  import java.nio.charset.Charset;
28  import java.nio.charset.StandardCharsets;
29  import java.util.Random;
30  
31  import org.apache.commons.codec.CodecPolicy;
32  import org.apache.commons.codec.DecoderException;
33  import org.apache.commons.codec.EncoderException;
34  import org.apache.commons.lang3.ArrayUtils;
35  import org.junit.jupiter.api.Test;
36  
37  /**
38   * Tests {@link Base16}.
39   */
40  class Base16Test {
41  
42      private static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
43  
44      private final Random random = new Random();
45  
46      /**
47       * Gets the random number generator.
48       *
49       * @return The random.
50       */
51      Random getRandom() {
52          return this.random;
53      }
54  
55      /**
56       * Test the Base16 implementation
57       */
58      @Test
59      void testBase16() {
60          final String content = "Hello World";
61          final byte[] encodedBytes = new Base16().encode(StringUtils.getBytesUtf8(content));
62          final String encodedContent = StringUtils.newStringUtf8(encodedBytes);
63          assertEquals("48656C6C6F20576F726C64", encodedContent, "encoding hello world");
64  
65          final byte[] decodedBytes = new Base16().decode(encodedBytes);
66          final String decodedContent = StringUtils.newStringUtf8(decodedBytes);
67          assertEquals(content, decodedContent, "decoding hello world");
68      }
69  
70      @Test
71      void testBase16AtBufferEnd() {
72          testBase16InBuffer(100, 0);
73      }
74  
75      @Test
76      void testBase16AtBufferMiddle() {
77          testBase16InBuffer(100, 100);
78      }
79  
80      @Test
81      void testBase16AtBufferStart() {
82          testBase16InBuffer(0, 100);
83      }
84  
85      private void testBase16InBuffer(final int startPasSize, final int endPadSize) {
86          final String content = "Hello World";
87          final String encodedContent;
88          final byte[] bytesUtf8 = StringUtils.getBytesUtf8(content);
89          byte[] buffer = ArrayUtils.addAll(bytesUtf8, new byte[endPadSize]);
90          buffer = ArrayUtils.addAll(new byte[startPasSize], buffer);
91          final byte[] encodedBytes = new Base16().encode(buffer, startPasSize, bytesUtf8.length);
92          encodedContent = StringUtils.newStringUtf8(encodedBytes);
93          assertEquals("48656C6C6F20576F726C64", encodedContent, "encoding hello world");
94      }
95  
96      @Test
97      void testBuilderSetLowerCaseDecodesOwnOutput() {
98          final Base16 base16 = Base16.builder().setLowerCase(true).get();
99          final byte[] data = { (byte) 0xab };
100         final byte[] encoded = base16.encode(data);
101         assertEquals("ab", new String(encoded, StandardCharsets.US_ASCII));
102         assertArrayEquals(data, base16.decode(encoded));
103     }
104 
105     @Test
106     void testByteToStringVariations() {
107         final Base16 base16 = new Base16();
108         final byte[] b1 = StringUtils.getBytesUtf8("Hello World");
109         final byte[] b2 = {};
110         final byte[] b3 = null;
111 
112         assertEquals("48656C6C6F20576F726C64", base16.encodeToString(b1), "byteToString Hello World");
113         assertEquals("48656C6C6F20576F726C64", StringUtils.newStringUtf8(new Base16().encode(b1)), "byteToString static Hello World");
114         assertEquals("", base16.encodeToString(b2), "byteToString \"\"");
115         assertEquals("", StringUtils.newStringUtf8(new Base16().encode(b2)), "byteToString static \"\"");
116         assertNull(base16.encodeToString(b3), "byteToString null");
117         assertNull(StringUtils.newStringUtf8(new Base16().encode(b3)), "byteToString static null");
118     }
119 
120     @Test
121     void testCheckEncodeLengthBounds() {
122         final Base16 base16 = new Base16();
123         assertThrows(IllegalArgumentException.class, () -> base16.encode(new byte[10], 0, 1 << 30));
124     }
125 
126     /**
127      * isBase16 throws RuntimeException on some non-Base16 bytes
128      */
129     @Test
130     void testCodec68() {
131         final byte[] x = { 'n', 'H', '=', '=', (byte) 0x9c };
132         final Base16 b16 = new Base16();
133         assertThrows(RuntimeException.class, () -> b16.decode(x));
134     }
135 
136     @Test
137     void testConstructor_LowerCase() {
138         final Base16 base16 = new Base16(true);
139         final byte[] encoded = base16.encode(BaseNTestData.DECODED);
140         final String expectedResult = Base16TestData.ENCODED_UTF8_LOWERCASE;
141         final String result = StringUtils.newStringUtf8(encoded);
142         assertEquals(expectedResult, result, "new Base16(true)");
143     }
144 
145     @Test
146     void testConstructor_LowerCase_DecodingPolicy() {
147         final Base16 base16 = new Base16(false, CodecPolicy.STRICT);
148         final byte[] encoded = base16.encode(BaseNTestData.DECODED);
149         final String expectedResult = Base16TestData.ENCODED_UTF8_UPPERCASE;
150         final String result = StringUtils.newStringUtf8(encoded);
151         assertEquals(result, expectedResult, "new base16(false, CodecPolicy.STRICT)");
152     }
153 
154     @Test
155     void testConstructors() {
156         new Base16();
157         new Base16(false);
158         new Base16(true);
159         new Base16(false, CodecPolicy.LENIENT);
160         new Base16(false, CodecPolicy.STRICT);
161     }
162 
163     @Test
164     void testCustomEncodeTableAffectsDecodeTable() {
165         final byte[] encodeTable = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
166         final byte tmp = encodeTable[0];
167         encodeTable[0] = encodeTable[1];
168         encodeTable[1] = tmp;
169         final Base16 base16 = Base16.builder().setEncodeTable(encodeTable).get();
170         final byte[] encoded = base16.encode(new byte[] { 1 });
171         assertEquals("10", new String(encoded, StandardCharsets.US_ASCII));
172         assertArrayEquals(new byte[] { 1 }, base16.decode(encoded));
173     }
174 
175     @Test
176     void testCustomEncodeTableRejectsDuplicates() {
177         final byte[] encodeTable = "00123456789ABCDE".getBytes(StandardCharsets.US_ASCII);
178         assertThrows(IllegalArgumentException.class, () -> Base16.builder().setEncodeTable(encodeTable));
179     }
180 
181     @Test
182     void testCustomEncodeTableRejectsInvalidLength() {
183         assertThrows(IllegalArgumentException.class,
184                 () -> Base16.builder().setEncodeTable("0123456789ABCDE".getBytes(StandardCharsets.US_ASCII)));
185     }
186 
187     @Test
188     void testDecodeSingleBytes() {
189         final String encoded = "556E74696C206E6578742074696D6521";
190 
191         final BaseNCodec.Context context = new BaseNCodec.Context();
192         final Base16 b16 = new Base16();
193 
194         final byte[] encocdedBytes = StringUtils.getBytesUtf8(encoded);
195 
196         // decode byte-by-byte
197         b16.decode(encocdedBytes, 0, 1, context);
198         b16.decode(encocdedBytes, 1, 1, context); // yields "U"
199         b16.decode(encocdedBytes, 2, 1, context);
200         b16.decode(encocdedBytes, 3, 1, context); // yields "n"
201 
202         // decode split hex-pairs
203         b16.decode(encocdedBytes, 4, 3, context); // yields "t"
204         b16.decode(encocdedBytes, 7, 3, context); // yields "il"
205         b16.decode(encocdedBytes, 10, 3, context); // yields " "
206 
207         // decode remaining
208         b16.decode(encocdedBytes, 13, 19, context); // yields "next time!"
209 
210         final byte[] decodedBytes = new byte[context.pos];
211         System.arraycopy(context.buffer, context.readPos, decodedBytes, 0, decodedBytes.length);
212         final String decoded = StringUtils.newStringUtf8(decodedBytes);
213 
214         assertEquals("Until next time!", decoded);
215     }
216 
217     @Test
218     void testDecodeSingleBytesOptimization() {
219         final BaseNCodec.Context context = new BaseNCodec.Context();
220         assertEquals(0, context.ibitWorkArea);
221         assertNull(context.buffer);
222 
223         final byte[] data = new byte[1];
224 
225         final Base16 b16 = new Base16();
226 
227         data[0] = (byte) 'E';
228         b16.decode(data, 0, 1, context);
229         assertEquals(15, context.ibitWorkArea);
230         assertNull(context.buffer);
231 
232         data[0] = (byte) 'F';
233         b16.decode(data, 0, 1, context);
234         assertEquals(0, context.ibitWorkArea);
235 
236         assertEquals((byte) 0xEF, context.buffer[0]);
237     }
238 
239     /**
240      * Test encode and decode of empty byte array.
241      */
242     @Test
243     void testEmptyBase16() {
244         byte[] empty = {};
245         byte[] result = new Base16().encode(empty);
246         assertEquals(0, result.length, "empty Base16 encode");
247         assertNull(new Base16().encode(null), "empty Base16 encode");
248         result = new Base16().encode(empty, 0, 1);
249         assertEquals(0, result.length, "empty Base16 encode with offset");
250         assertNull(new Base16().encode(null), "empty Base16 encode with offset");
251 
252         empty = new byte[0];
253         result = new Base16().decode(empty);
254         assertEquals(0, result.length, "empty Base16 decode");
255         assertNull(new Base16().decode((byte[]) null), "empty Base16 encode");
256     }
257 
258     // encode/decode a large random array
259     @Test
260     void testEncodeDecodeRandom() {
261         for (int i = 1; i < 5; i++) {
262             final int len = getRandom().nextInt(10000) + 1;
263             final byte[] data = new byte[len];
264             getRandom().nextBytes(data);
265             final byte[] enc = new Base16().encode(data);
266             final byte[] data2 = new Base16().decode(enc);
267             assertArrayEquals(data, data2);
268         }
269     }
270 
271     // encode/decode random arrays from size 0 to size 11
272     @Test
273     void testEncodeDecodeSmall() {
274         for (int i = 0; i < 12; i++) {
275             final byte[] data = new byte[i];
276             getRandom().nextBytes(data);
277             final byte[] enc = new Base16().encode(data);
278             final byte[] data2 = new Base16().decode(enc);
279             assertArrayEquals(data, data2, toString(data) + " equals " + toString(data2));
280         }
281     }
282 
283     @Test
284     void testIsInAlphabet() {
285         // invalid bounds
286         Base16 b16 = Base16.builder().setLowerCase(true).get();
287         assertFalse(b16.isInAlphabet((byte) 0));
288         assertFalse(b16.isInAlphabet((byte) 1));
289         assertFalse(b16.isInAlphabet((byte) -1));
290         assertFalse(b16.isInAlphabet((byte) -15));
291         assertFalse(b16.isInAlphabet((byte) -16));
292         assertFalse(b16.isInAlphabet((byte) 128));
293         assertFalse(b16.isInAlphabet((byte) 255));
294         // lower-case
295         b16 = new Base16(true);
296         for (char c = '0'; c <= '9'; c++) {
297             assertTrue(b16.isInAlphabet((byte) c));
298         }
299         for (char c = 'a'; c <= 'f'; c++) {
300             assertTrue(b16.isInAlphabet((byte) c));
301         }
302         for (char c = 'A'; c <= 'F'; c++) {
303             assertFalse(b16.isInAlphabet((byte) c));
304         }
305         assertFalse(b16.isInAlphabet((byte) ('0' - 1)));
306         assertFalse(b16.isInAlphabet((byte) ('9' + 1)));
307         assertFalse(b16.isInAlphabet((byte) ('a' - 1)));
308         assertFalse(b16.isInAlphabet((byte) ('f' + 1)));
309         assertFalse(b16.isInAlphabet((byte) ('z' + 1)));
310         // upper-case
311         b16 = new Base16(false);
312         for (char c = '0'; c <= '9'; c++) {
313             assertTrue(b16.isInAlphabet((byte) c));
314         }
315         for (char c = 'a'; c <= 'f'; c++) {
316             assertFalse(b16.isInAlphabet((byte) c));
317         }
318         for (char c = 'A'; c <= 'F'; c++) {
319             assertTrue(b16.isInAlphabet((byte) c));
320         }
321         assertFalse(b16.isInAlphabet((byte) ('0' - 1)));
322         assertFalse(b16.isInAlphabet((byte) ('9' + 1)));
323         assertFalse(b16.isInAlphabet((byte) ('A' - 1)));
324         assertFalse(b16.isInAlphabet((byte) ('F' + 1)));
325         assertFalse(b16.isInAlphabet((byte) ('Z' + 1)));
326     }
327 
328     @Test
329     void testKnownDecodings() {
330         assertEquals("The quick brown fox jumped over the lazy dogs.", new String(new Base16(true)
331                 .decode("54686520717569636b2062726f776e20666f78206a756d706564206f76657220746865206c617a7920646f67732e".getBytes(CHARSET_UTF8))));
332         assertEquals("It was the best of times, it was the worst of times.", new String(new Base16(true)
333                 .decode("497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732e".getBytes(CHARSET_UTF8))));
334         assertEquals("http://jakarta.apache.org/commmons",
335                 new String(new Base16(true).decode("687474703a2f2f6a616b617274612e6170616368652e6f72672f636f6d6d6d6f6e73".getBytes(CHARSET_UTF8))));
336         assertEquals("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz", new String(new Base16(true)
337                 .decode("4161426243634464456546664767486849694a6a4b6b4c6c4d6d4e6e4f6f50705171527253735474557556765777587859795a7a".getBytes(CHARSET_UTF8))));
338         assertEquals("{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }",
339                 new String(new Base16(true).decode("7b20302c20312c20322c20332c20342c20352c20362c20372c20382c2039207d".getBytes(CHARSET_UTF8))));
340         assertEquals("xyzzy!", new String(new Base16(true).decode("78797a7a7921".getBytes(CHARSET_UTF8))));
341     }
342 
343     @Test
344     void testKnownEncodings() {
345         assertEquals("54686520717569636b2062726f776e20666f78206a756d706564206f76657220746865206c617a7920646f67732e",
346                 new String(new Base16(true).encode("The quick brown fox jumped over the lazy dogs.".getBytes(CHARSET_UTF8))));
347         assertEquals("497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732e",
348                 new String(new Base16(true).encode("It was the best of times, it was the worst of times.".getBytes(CHARSET_UTF8))));
349         assertEquals("687474703a2f2f6a616b617274612e6170616368652e6f72672f636f6d6d6d6f6e73",
350                 new String(new Base16(true).encode("http://jakarta.apache.org/commmons".getBytes(CHARSET_UTF8))));
351         assertEquals("4161426243634464456546664767486849694a6a4b6b4c6c4d6d4e6e4f6f50705171527253735474557556765777587859795a7a",
352                 new String(new Base16(true).encode("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz".getBytes(CHARSET_UTF8))));
353         assertEquals("7b20302c20312c20322c20332c20342c20352c20362c20372c20382c2039207d",
354                 new String(new Base16(true).encode("{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }".getBytes(CHARSET_UTF8))));
355         assertEquals("78797a7a7921", new String(new Base16(true).encode("xyzzy!".getBytes(CHARSET_UTF8))));
356     }
357 
358     @Test
359     void testLenientDecoding() {
360         final String encoded = "aabbccdde"; // Note the trailing `e` which does not make up a hex-pair and so is only 1/2 byte
361 
362         final Base16 b16 = new Base16(true, CodecPolicy.LENIENT);
363         assertEquals(CodecPolicy.LENIENT, b16.getCodecPolicy());
364 
365         final byte[] decoded = b16.decode(StringUtils.getBytesUtf8(encoded));
366         assertArrayEquals(new byte[] { (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd }, decoded);
367     }
368 
369     @Test
370     void testNonBase16Test() {
371         final byte[] invalidEncodedChars = { '/', ':', '@', 'G', '%', '`', 'g' };
372 
373         final byte[] encoded = new byte[1];
374         for (final byte invalidEncodedChar : invalidEncodedChars) {
375             encoded[0] = invalidEncodedChar;
376             assertThrows(IllegalArgumentException.class, () -> new Base16().decode(encoded), "Invalid Base16 char: " + (char) invalidEncodedChar);
377         }
378     }
379 
380     @Test
381     void testObjectDecodeWithInvalidParameter() {
382         assertThrows(DecoderException.class, () -> new Base16().decode(Integer.valueOf(5)));
383     }
384 
385     @Test
386     void testObjectDecodeWithValidParameter() throws Exception {
387         final String original = "Hello World!";
388         final Object o = new Base16().encode(original.getBytes(CHARSET_UTF8));
389 
390         final Base16 b16 = new Base16();
391         final Object oDecoded = b16.decode(o);
392         final byte[] baDecoded = (byte[]) oDecoded;
393         final String dest = new String(baDecoded);
394 
395         assertEquals(original, dest, "dest string does not equal original");
396     }
397 
398     @Test
399     void testObjectEncode() {
400         final Base16 b16 = new Base16();
401         assertEquals(new String(b16.encode("Hello World".getBytes(CHARSET_UTF8))), "48656C6C6F20576F726C64");
402     }
403 
404     @Test
405     void testObjectEncodeWithInvalidParameter() {
406         assertThrows(EncoderException.class, () -> new Base16().encode("Yadayadayada"));
407     }
408 
409     @Test
410     void testObjectEncodeWithValidParameter() throws Exception {
411         final String original = "Hello World!";
412         final Object origObj = original.getBytes(CHARSET_UTF8);
413 
414         final Object oEncoded = new Base16().encode(origObj);
415         final byte[] bArray = new Base16().decode((byte[]) oEncoded);
416         final String dest = new String(bArray);
417 
418         assertEquals(original, dest, "dest string does not equal original");
419     }
420 
421     @Test
422     void testOddEvenDecoding() {
423         final String encoded = "4142434445";
424 
425         final BaseNCodec.Context context = new BaseNCodec.Context();
426         final Base16 base16 = new Base16();
427 
428         final byte[] encodedBytes = StringUtils.getBytesUtf8(encoded);
429 
430         // pass odd, then even, then odd amount of data
431         base16.decode(encodedBytes, 0, 3, context);
432         base16.decode(encodedBytes, 3, 4, context);
433         base16.decode(encodedBytes, 7, 3, context);
434 
435         final byte[] decodedBytes = new byte[context.pos];
436         System.arraycopy(context.buffer, context.readPos, decodedBytes, 0, decodedBytes.length);
437         final String decoded = StringUtils.newStringUtf8(decodedBytes);
438 
439         assertEquals("ABCDE", decoded);
440     }
441 
442     @Test
443     void testPairs() {
444         assertEquals("0000", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0 })));
445         assertEquals("0001", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 1 })));
446         assertEquals("0002", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 2 })));
447         assertEquals("0003", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 3 })));
448         assertEquals("0004", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 4 })));
449         assertEquals("0005", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 5 })));
450         assertEquals("0006", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 6 })));
451         assertEquals("0007", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 7 })));
452         assertEquals("0008", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 8 })));
453         assertEquals("0009", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 9 })));
454         assertEquals("000A", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 10 })));
455         assertEquals("000B", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 11 })));
456         assertEquals("000C", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 12 })));
457         assertEquals("000D", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 13 })));
458         assertEquals("000E", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 14 })));
459         assertEquals("000F", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 15 })));
460         assertEquals("0010", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 16 })));
461         assertEquals("0011", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 17 })));
462         for (int i = -128; i <= 127; i++) {
463             final byte[] test = { (byte) i, (byte) i };
464             assertArrayEquals(test, new Base16().decode(new Base16().encode(test)));
465         }
466     }
467 
468     @Test
469     void testSingletons() {
470         assertEquals("00", new String(new Base16().encode(new byte[] { (byte) 0 })));
471         assertEquals("01", new String(new Base16().encode(new byte[] { (byte) 1 })));
472         assertEquals("02", new String(new Base16().encode(new byte[] { (byte) 2 })));
473         assertEquals("03", new String(new Base16().encode(new byte[] { (byte) 3 })));
474         assertEquals("04", new String(new Base16().encode(new byte[] { (byte) 4 })));
475         assertEquals("05", new String(new Base16().encode(new byte[] { (byte) 5 })));
476         assertEquals("06", new String(new Base16().encode(new byte[] { (byte) 6 })));
477         assertEquals("07", new String(new Base16().encode(new byte[] { (byte) 7 })));
478         assertEquals("08", new String(new Base16().encode(new byte[] { (byte) 8 })));
479         assertEquals("09", new String(new Base16().encode(new byte[] { (byte) 9 })));
480         assertEquals("0A", new String(new Base16().encode(new byte[] { (byte) 10 })));
481         assertEquals("0B", new String(new Base16().encode(new byte[] { (byte) 11 })));
482         assertEquals("0C", new String(new Base16().encode(new byte[] { (byte) 12 })));
483         assertEquals("0D", new String(new Base16().encode(new byte[] { (byte) 13 })));
484         assertEquals("0E", new String(new Base16().encode(new byte[] { (byte) 14 })));
485         assertEquals("0F", new String(new Base16().encode(new byte[] { (byte) 15 })));
486         assertEquals("10", new String(new Base16().encode(new byte[] { (byte) 16 })));
487         assertEquals("11", new String(new Base16().encode(new byte[] { (byte) 17 })));
488         assertEquals("12", new String(new Base16().encode(new byte[] { (byte) 18 })));
489         assertEquals("13", new String(new Base16().encode(new byte[] { (byte) 19 })));
490         assertEquals("14", new String(new Base16().encode(new byte[] { (byte) 20 })));
491         assertEquals("15", new String(new Base16().encode(new byte[] { (byte) 21 })));
492         assertEquals("16", new String(new Base16().encode(new byte[] { (byte) 22 })));
493         assertEquals("17", new String(new Base16().encode(new byte[] { (byte) 23 })));
494         assertEquals("18", new String(new Base16().encode(new byte[] { (byte) 24 })));
495         assertEquals("19", new String(new Base16().encode(new byte[] { (byte) 25 })));
496         assertEquals("1A", new String(new Base16().encode(new byte[] { (byte) 26 })));
497         assertEquals("1B", new String(new Base16().encode(new byte[] { (byte) 27 })));
498         assertEquals("1C", new String(new Base16().encode(new byte[] { (byte) 28 })));
499         assertEquals("1D", new String(new Base16().encode(new byte[] { (byte) 29 })));
500         assertEquals("1E", new String(new Base16().encode(new byte[] { (byte) 30 })));
501         assertEquals("1F", new String(new Base16().encode(new byte[] { (byte) 31 })));
502         assertEquals("20", new String(new Base16().encode(new byte[] { (byte) 32 })));
503         assertEquals("21", new String(new Base16().encode(new byte[] { (byte) 33 })));
504         assertEquals("22", new String(new Base16().encode(new byte[] { (byte) 34 })));
505         assertEquals("23", new String(new Base16().encode(new byte[] { (byte) 35 })));
506         assertEquals("24", new String(new Base16().encode(new byte[] { (byte) 36 })));
507         assertEquals("25", new String(new Base16().encode(new byte[] { (byte) 37 })));
508         assertEquals("26", new String(new Base16().encode(new byte[] { (byte) 38 })));
509         assertEquals("27", new String(new Base16().encode(new byte[] { (byte) 39 })));
510         assertEquals("28", new String(new Base16().encode(new byte[] { (byte) 40 })));
511         assertEquals("29", new String(new Base16().encode(new byte[] { (byte) 41 })));
512         assertEquals("2A", new String(new Base16().encode(new byte[] { (byte) 42 })));
513         assertEquals("2B", new String(new Base16().encode(new byte[] { (byte) 43 })));
514         assertEquals("2C", new String(new Base16().encode(new byte[] { (byte) 44 })));
515         assertEquals("2D", new String(new Base16().encode(new byte[] { (byte) 45 })));
516         assertEquals("2E", new String(new Base16().encode(new byte[] { (byte) 46 })));
517         assertEquals("2F", new String(new Base16().encode(new byte[] { (byte) 47 })));
518         assertEquals("30", new String(new Base16().encode(new byte[] { (byte) 48 })));
519         assertEquals("31", new String(new Base16().encode(new byte[] { (byte) 49 })));
520         assertEquals("32", new String(new Base16().encode(new byte[] { (byte) 50 })));
521         assertEquals("33", new String(new Base16().encode(new byte[] { (byte) 51 })));
522         assertEquals("34", new String(new Base16().encode(new byte[] { (byte) 52 })));
523         assertEquals("35", new String(new Base16().encode(new byte[] { (byte) 53 })));
524         assertEquals("36", new String(new Base16().encode(new byte[] { (byte) 54 })));
525         assertEquals("37", new String(new Base16().encode(new byte[] { (byte) 55 })));
526         assertEquals("38", new String(new Base16().encode(new byte[] { (byte) 56 })));
527         assertEquals("39", new String(new Base16().encode(new byte[] { (byte) 57 })));
528         assertEquals("3A", new String(new Base16().encode(new byte[] { (byte) 58 })));
529         assertEquals("3B", new String(new Base16().encode(new byte[] { (byte) 59 })));
530         assertEquals("3C", new String(new Base16().encode(new byte[] { (byte) 60 })));
531         assertEquals("3D", new String(new Base16().encode(new byte[] { (byte) 61 })));
532         assertEquals("3E", new String(new Base16().encode(new byte[] { (byte) 62 })));
533         assertEquals("3F", new String(new Base16().encode(new byte[] { (byte) 63 })));
534         assertEquals("40", new String(new Base16().encode(new byte[] { (byte) 64 })));
535         assertEquals("41", new String(new Base16().encode(new byte[] { (byte) 65 })));
536         assertEquals("42", new String(new Base16().encode(new byte[] { (byte) 66 })));
537         assertEquals("43", new String(new Base16().encode(new byte[] { (byte) 67 })));
538         assertEquals("44", new String(new Base16().encode(new byte[] { (byte) 68 })));
539         assertEquals("45", new String(new Base16().encode(new byte[] { (byte) 69 })));
540         assertEquals("46", new String(new Base16().encode(new byte[] { (byte) 70 })));
541         assertEquals("47", new String(new Base16().encode(new byte[] { (byte) 71 })));
542         assertEquals("48", new String(new Base16().encode(new byte[] { (byte) 72 })));
543         assertEquals("49", new String(new Base16().encode(new byte[] { (byte) 73 })));
544         assertEquals("4A", new String(new Base16().encode(new byte[] { (byte) 74 })));
545         assertEquals("4B", new String(new Base16().encode(new byte[] { (byte) 75 })));
546         assertEquals("4C", new String(new Base16().encode(new byte[] { (byte) 76 })));
547         assertEquals("4D", new String(new Base16().encode(new byte[] { (byte) 77 })));
548         assertEquals("4E", new String(new Base16().encode(new byte[] { (byte) 78 })));
549         assertEquals("4F", new String(new Base16().encode(new byte[] { (byte) 79 })));
550         assertEquals("50", new String(new Base16().encode(new byte[] { (byte) 80 })));
551         assertEquals("51", new String(new Base16().encode(new byte[] { (byte) 81 })));
552         assertEquals("52", new String(new Base16().encode(new byte[] { (byte) 82 })));
553         assertEquals("53", new String(new Base16().encode(new byte[] { (byte) 83 })));
554         assertEquals("54", new String(new Base16().encode(new byte[] { (byte) 84 })));
555         assertEquals("55", new String(new Base16().encode(new byte[] { (byte) 85 })));
556         assertEquals("56", new String(new Base16().encode(new byte[] { (byte) 86 })));
557         assertEquals("57", new String(new Base16().encode(new byte[] { (byte) 87 })));
558         assertEquals("58", new String(new Base16().encode(new byte[] { (byte) 88 })));
559         assertEquals("59", new String(new Base16().encode(new byte[] { (byte) 89 })));
560         assertEquals("5A", new String(new Base16().encode(new byte[] { (byte) 90 })));
561         assertEquals("5B", new String(new Base16().encode(new byte[] { (byte) 91 })));
562         assertEquals("5C", new String(new Base16().encode(new byte[] { (byte) 92 })));
563         assertEquals("5D", new String(new Base16().encode(new byte[] { (byte) 93 })));
564         assertEquals("5E", new String(new Base16().encode(new byte[] { (byte) 94 })));
565         assertEquals("5F", new String(new Base16().encode(new byte[] { (byte) 95 })));
566         assertEquals("60", new String(new Base16().encode(new byte[] { (byte) 96 })));
567         assertEquals("61", new String(new Base16().encode(new byte[] { (byte) 97 })));
568         assertEquals("62", new String(new Base16().encode(new byte[] { (byte) 98 })));
569         assertEquals("63", new String(new Base16().encode(new byte[] { (byte) 99 })));
570         assertEquals("64", new String(new Base16().encode(new byte[] { (byte) 100 })));
571         assertEquals("65", new String(new Base16().encode(new byte[] { (byte) 101 })));
572         assertEquals("66", new String(new Base16().encode(new byte[] { (byte) 102 })));
573         assertEquals("67", new String(new Base16().encode(new byte[] { (byte) 103 })));
574         assertEquals("68", new String(new Base16().encode(new byte[] { (byte) 104 })));
575         for (int i = -128; i <= 127; i++) {
576             final byte[] test = { (byte) i };
577             assertArrayEquals(test, new Base16().decode(new Base16().encode(test)));
578         }
579     }
580 
581     @Test
582     void testStrictDecoding() {
583         final String encoded = "aabbccdde"; // Note the trailing `e` which does not make up a hex-pair and so is only 1/2 byte
584 
585         final Base16 b16 = new Base16(true, CodecPolicy.STRICT);
586         assertEquals(CodecPolicy.STRICT, b16.getCodecPolicy());
587         assertThrows(IllegalArgumentException.class, () -> b16.decode(StringUtils.getBytesUtf8(encoded)));
588     }
589 
590     @Test
591     void testStringToByteVariations() throws DecoderException {
592         final Base16 base16 = new Base16();
593         final String s1 = "48656C6C6F20576F726C64";
594         final String s2 = "";
595         final String s3 = null;
596 
597         assertEquals("Hello World", StringUtils.newStringUtf8(base16.decode(s1)), "StringToByte Hello World");
598         assertEquals("Hello World", StringUtils.newStringUtf8((byte[]) new Base16().decode((Object) s1)), "StringToByte Hello World");
599         assertEquals("Hello World", StringUtils.newStringUtf8(new Base16().decode(s1)), "StringToByte static Hello World");
600         assertEquals("", StringUtils.newStringUtf8(new Base16().decode(s2)), "StringToByte \"\"");
601         assertEquals("", StringUtils.newStringUtf8(new Base16().decode(s2)), "StringToByte static \"\"");
602         assertNull(StringUtils.newStringUtf8(new Base16().decode(s3)), "StringToByte null");
603         assertNull(StringUtils.newStringUtf8(new Base16().decode(s3)), "StringToByte static null");
604     }
605 
606     @Test
607     void testTriplets() {
608         assertEquals("000000", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 0 })));
609         assertEquals("000001", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 1 })));
610         assertEquals("000002", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 2 })));
611         assertEquals("000003", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 3 })));
612         assertEquals("000004", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 4 })));
613         assertEquals("000005", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 5 })));
614         assertEquals("000006", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 6 })));
615         assertEquals("000007", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 7 })));
616         assertEquals("000008", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 8 })));
617         assertEquals("000009", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 9 })));
618         assertEquals("00000A", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 10 })));
619         assertEquals("00000B", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 11 })));
620         assertEquals("00000C", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 12 })));
621         assertEquals("00000D", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 13 })));
622         assertEquals("00000E", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 14 })));
623         assertEquals("00000F", new String(new Base16().encode(new byte[] { (byte) 0, (byte) 0, (byte) 15 })));
624     }
625 
626     private String toString(final byte[] data) {
627         final StringBuilder buf = new StringBuilder();
628         for (int i = 0; i < data.length; i++) {
629             buf.append(data[i]);
630             if (i != data.length - 1) {
631                 buf.append(",");
632             }
633         }
634         return buf.toString();
635     }
636 }