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.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  import static org.junit.jupiter.api.Assertions.fail;
28  
29  import java.math.BigInteger;
30  import java.nio.charset.Charset;
31  import java.nio.charset.StandardCharsets;
32  import java.util.Arrays;
33  import java.util.Random;
34  
35  import org.apache.commons.codec.CodecPolicy;
36  import org.apache.commons.codec.DecoderException;
37  import org.apache.commons.codec.EncoderException;
38  import org.apache.commons.lang3.ArrayUtils;
39  import org.junit.jupiter.api.Assumptions;
40  import org.junit.jupiter.api.Test;
41  
42  /**
43   * Tests {@link Base64}.
44   *
45   * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
46   */
47  class Base64Test {
48  
49      private static final String FOX_BASE64 = "VGhlIH@$#$@%F1aWN@#@#@@rIGJyb3duIGZve\n\r\t%#%#%#%CBqd##$#$W1wZWQgb3ZlciB0aGUgbGF6eSBkb2dzLg==";
50  
51      private static final String FOX_TEXT = "The quick brown fox jumped over the lazy dogs.";
52  
53      private static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
54  
55      /**
56       * Example test cases with valid characters but impossible combinations of
57       * trailing characters (i.e. cannot be created during encoding).
58       */
59      static final String[] BASE64_IMPOSSIBLE_CASES = {
60          "ZE==",
61          "ZmC=",
62          "Zm9vYE==",
63          "Zm9vYmC=",
64          "AB",
65      };
66  
67      /**
68       * Copy of the standard base-64 encoding table. Used to test decoding the final
69       * character of encoded bytes.
70       */
71      private static final byte[] STANDARD_ENCODE_TABLE = {
72              'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
73              'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
74              'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
75              'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
76              '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
77      };
78  
79      /**
80       * Test base 64 decoding of the final trailing bits. Trailing encoded bytes
81       * cannot fit exactly into 6-bit characters so the last character has a limited
82       * alphabet where the final bits are zero. This asserts that illegal final
83       * characters throw an exception when decoding.
84       *
85       * @param nbits the number of trailing bits (must be a factor of 6 and {@code <24})
86       */
87      private static void assertBase64DecodingOfTrailingBits(final int nbits) {
88          final Base64 codec = new Base64(0, null, false, CodecPolicy.STRICT);
89          // Requires strict decoding
90          assertTrue(codec.isStrictDecoding());
91          assertEquals(CodecPolicy.STRICT, codec.getCodecPolicy());
92          // A lenient decoder should not re-encode to the same bytes
93          final Base64 defaultCodec = new Base64();
94          assertFalse(defaultCodec.isStrictDecoding());
95          assertEquals(CodecPolicy.LENIENT, defaultCodec.getCodecPolicy());
96  
97          // Create the encoded bytes. The first characters must be valid so fill with 'zero'
98          // then pad to the block size.
99          final int length = nbits / 6;
100         final byte[] encoded = new byte[4];
101         Arrays.fill(encoded, 0, length, STANDARD_ENCODE_TABLE[0]);
102         Arrays.fill(encoded, length, encoded.length, (byte) '=');
103         // Compute how many bits would be discarded from 8-bit bytes
104         final int discard = nbits % 8;
105         final int emptyBitsMask = (1 << discard) - 1;
106         // Special case when an impossible number of trailing characters
107         final boolean invalid = length == 1;
108         // Enumerate all 64 possible final characters in the last position
109         final int last = length - 1;
110         for (int i = 0; i < 64; i++) {
111             encoded[last] = STANDARD_ENCODE_TABLE[i];
112             // If the lower bits are set we expect an exception. This is not a valid
113             // final character.
114             if (invalid || (i & emptyBitsMask) != 0) {
115                 assertThrows(IllegalArgumentException.class, () -> codec.decode(encoded),
116                         "Final base-64 digit should not be allowed");
117                 // The default lenient mode should decode this
118                 final byte[] decoded = defaultCodec.decode(encoded);
119                 // Re-encoding should not match the original array as it was invalid
120                 assertFalse(Arrays.equals(encoded, defaultCodec.encode(decoded)));
121             } else {
122                 // Otherwise this should decode
123                 final byte[] decoded = codec.decode(encoded);
124                 // Compute the bits that were encoded. This should match the final decoded byte.
125                 final int bitsEncoded = i >> discard;
126                 assertEquals(bitsEncoded, decoded[decoded.length - 1], "Invalid decoding of last character");
127                 // Re-encoding should match the original array (requires the same padding character)
128                 assertArrayEquals(encoded, codec.encode(decoded));
129             }
130         }
131     }
132 
133     private final Random random = new Random();
134 
135     /**
136      * @return the random.
137      */
138     public Random getRandom() {
139         return this.random;
140     }
141 
142     /**
143      * Test the Base64 implementation
144      */
145     @Test
146     void testBase64() {
147         final String content = "Hello World";
148         String encodedContent;
149         byte[] encodedBytes = Base64.encodeBase64(StringUtils.getBytesUtf8(content));
150         encodedContent = StringUtils.newStringUtf8(encodedBytes);
151         assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world");
152 
153         Base64 b64 = new Base64(BaseNCodec.MIME_CHUNK_SIZE, null); // null
154                                                                     // lineSeparator
155                                                                     // same as
156                                                                     // saying
157                                                                     // no-chunking
158         encodedBytes = b64.encode(StringUtils.getBytesUtf8(content));
159         encodedContent = StringUtils.newStringUtf8(encodedBytes);
160         assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world");
161 
162         b64 = new Base64(0, null); // null lineSeparator same as saying
163                                     // no-chunking
164         encodedBytes = b64.encode(StringUtils.getBytesUtf8(content));
165         encodedContent = StringUtils.newStringUtf8(encodedBytes);
166         assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world");
167 
168         // bogus characters to decode (to skip actually) {e-acute*6}
169         final byte[] decode = b64.decode("SGVsbG{\u00e9\u00e9\u00e9\u00e9\u00e9\u00e9}8gV29ybGQ=");
170         final String decodeString = StringUtils.newStringUtf8(decode);
171         assertEquals("Hello World", decodeString, "decode hello world");
172     }
173 
174     @Test
175     void testBase64AtBufferEnd() {
176         testBase64InBuffer(100, 0);
177     }
178 
179     @Test
180     void testBase64AtBufferMiddle() {
181         testBase64InBuffer(100, 100);
182     }
183 
184     @Test
185     void testBase64AtBufferStart() {
186         testBase64InBuffer(0, 100);
187     }
188 
189     @Test
190     void testBase64DecodingOfTrailing12Bits() {
191         assertBase64DecodingOfTrailingBits(12);
192     }
193 
194     @Test
195     void testBase64DecodingOfTrailing18Bits() {
196         assertBase64DecodingOfTrailingBits(18);
197     }
198 
199     @Test
200     void testBase64DecodingOfTrailing6Bits() {
201         assertBase64DecodingOfTrailingBits(6);
202     }
203 
204     @Test
205     void testBase64ImpossibleSamples() {
206         final Base64 codec = new Base64(0, null, false, CodecPolicy.STRICT);
207         for (final String s : BASE64_IMPOSSIBLE_CASES) {
208             assertThrows(IllegalArgumentException.class, () -> codec.decode(s));
209         }
210     }
211 
212     private void testBase64InBuffer(final int startPasSize, final int endPadSize) {
213         final String content = "Hello World";
214         final String encodedContent;
215         final byte[] bytesUtf8 = StringUtils.getBytesUtf8(content);
216         byte[] buffer = ArrayUtils.addAll(bytesUtf8, new byte[endPadSize]);
217         buffer = ArrayUtils.addAll(new byte[startPasSize], buffer);
218         final byte[] encodedBytes = new Base64().encode(buffer, startPasSize, bytesUtf8.length);
219         encodedContent = StringUtils.newStringUtf8(encodedBytes);
220         assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world");
221     }
222 
223     @Test
224     void testBuilderCodecPolicy() {
225         assertEquals(CodecPolicy.LENIENT, Base64.builder().get().getCodecPolicy());
226         assertEquals(CodecPolicy.LENIENT, Base64.builder().setDecodingPolicy(CodecPolicy.LENIENT).get().getCodecPolicy());
227         assertEquals(CodecPolicy.STRICT, Base64.builder().setDecodingPolicy(CodecPolicy.STRICT).get().getCodecPolicy());
228         assertEquals(CodecPolicy.LENIENT, Base64.builder().setDecodingPolicy(CodecPolicy.STRICT).setDecodingPolicy(null).get().getCodecPolicy());
229         assertEquals(CodecPolicy.LENIENT, Base64.builder().setDecodingPolicy(null).get().getCodecPolicy());
230     }
231 
232     @Test
233     void testBuilderLineAttributes() {
234         assertNull(Base64.builder().get().getLineSeparator());
235         assertNull(Base64.builder().setLineSeparator(BaseNCodec.CHUNK_SEPARATOR).get().getLineSeparator());
236         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(4).setLineSeparator(BaseNCodec.CHUNK_SEPARATOR).get().getLineSeparator());
237         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(4).setLineSeparator(null).get().getLineSeparator());
238         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(10).setLineSeparator(null).get().getLineSeparator());
239         assertNull(Base64.builder().setLineLength(-1).setLineSeparator(null).get().getLineSeparator());
240         assertNull(Base64.builder().setLineLength(0).setLineSeparator(null).get().getLineSeparator());
241         assertArrayEquals(new byte[] { 1 }, Base64.builder().setLineLength(4).setLineSeparator((byte) 1).get().getLineSeparator());
242         assertEquals("Zm94\r\n", Base64.builder().setLineLength(4).get().encodeToString("fox".getBytes(CHARSET_UTF8)));
243     }
244 
245     @Test
246     void testBuilderPadingByte() {
247         assertNull(Base64.builder().get().getLineSeparator());
248         assertNull(Base64.builder().setLineSeparator(BaseNCodec.CHUNK_SEPARATOR).get().getLineSeparator());
249         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(4).setLineSeparator(BaseNCodec.CHUNK_SEPARATOR).get().getLineSeparator());
250         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(4).setLineSeparator(null).get().getLineSeparator());
251         assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, Base64.builder().setLineLength(10).setLineSeparator(null).get().getLineSeparator());
252         assertNull(Base64.builder().setLineLength(-1).setLineSeparator(null).get().getLineSeparator());
253         assertNull(Base64.builder().setLineLength(0).setLineSeparator(null).get().getLineSeparator());
254         assertArrayEquals(new byte[] { 1 }, Base64.builder().setLineLength(4).setLineSeparator((byte) 1).get().getLineSeparator());
255         assertEquals("VGhlIGJyb3duIGZveA==", Base64.builder().get().encodeToString("The brown fox".getBytes(CHARSET_UTF8)));
256         assertEquals("VGhlIGJyb3duIGZveA__", Base64.builder().setPadding((byte) '_').get().encodeToString("The brown fox".getBytes(CHARSET_UTF8)));
257     }
258 
259     @Test
260     void testBuilderUrlSafe() {
261         assertFalse(Base64.builder().get().isUrlSafe());
262         assertFalse(Base64.builder().setUrlSafe(false).get().isUrlSafe());
263         assertFalse(Base64.builder().setUrlSafe(true).setUrlSafe(false).get().isUrlSafe());
264         assertTrue(Base64.builder().setUrlSafe(false).setUrlSafe(true).get().isUrlSafe());
265     }
266 
267     @Test
268     void testByteToStringVariations() throws DecoderException {
269         final Base64 base64 = new Base64(0);
270         final byte[] b1 = StringUtils.getBytesUtf8("Hello World");
271         final byte[] b2 = {};
272         final byte[] b3 = null;
273         final byte[] b4 = Hex.decodeHex("2bf7cc2701fe4397b49ebeed5acc7090"); // for url-safe tests
274 
275         assertEquals("SGVsbG8gV29ybGQ=", base64.encodeToString(b1), "byteToString Hello World");
276         assertEquals("SGVsbG8gV29ybGQ=", Base64.encodeBase64String(b1), "byteToString static Hello World");
277         assertEquals("", base64.encodeToString(b2), "byteToString \"\"");
278         assertEquals("", Base64.encodeBase64String(b2), "byteToString static \"\"");
279         assertNull(base64.encodeToString(b3), "byteToString null");
280         assertNull(Base64.encodeBase64String(b3), "byteToString static null");
281         assertEquals("K/fMJwH+Q5e0nr7tWsxwkA==", base64.encodeToString(b4), "byteToString UUID");
282         assertEquals("K/fMJwH+Q5e0nr7tWsxwkA==", Base64.encodeBase64String(b4), "byteToString static UUID");
283         assertEquals("K_fMJwH-Q5e0nr7tWsxwkA", Base64.encodeBase64URLSafeString(b4), "byteToString static-url-safe UUID");
284     }
285 
286     /**
287      * Tests Base64.encodeBase64().
288      */
289     @Test
290     void testChunkedEncodeMultipleOf76() {
291         final byte[] expectedEncode = Base64.encodeBase64(BaseNTestData.DECODED, true);
292         // convert to "\r\n" so we're equal to the old openssl encoding test
293         // stored
294         // in Base64TestData.ENCODED_76_CHARS_PER_LINE:
295         final String actualResult = Base64TestData.ENCODED_76_CHARS_PER_LINE.replace("\n", "\r\n");
296         final byte[] actualEncode = StringUtils.getBytesUtf8(actualResult);
297         assertArrayEquals(expectedEncode, actualEncode, "chunkedEncodeMultipleOf76");
298     }
299 
300     @Test
301     void testCodec112() { // size calculation assumes always chunked
302         final byte[] in = { 0 };
303         final byte[] out = Base64.encodeBase64(in);
304         Base64.encodeBase64(in, false, false, out.length);
305         // TODO Assert??
306     }
307 
308     /**
309      * Tests <a href="https://issues.apache.org/jira/browse/CODEC-263">CODEC-263</a>.
310      */
311     @Test
312     void testCodec263() {
313       Base64.decodeBase64("publishMessage");
314       assertTrue(Base64.isBase64("publishMessage"));
315     }
316 
317     /**
318      * Test for CODEC-265: Encode a 1GiB file.
319      *
320      * @see <a href="https://issues.apache.org/jira/projects/CODEC/issues/CODEC-265">CODEC-265</a>
321      */
322     @Test
323     void testCodec265() {
324         // 1GiB file to encode: 2^30 bytes
325         final int size1GiB = 1 << 30;
326 
327         // Expecting a size of 4 output bytes per 3 input bytes plus the trailing bytes
328         // padded to a block size of 4.
329         final int blocks = (int) Math.ceil(size1GiB / 3.0);
330         final int expectedLength = 4 * blocks;
331 
332         // This test is memory hungry. Check we can run it.
333         final long presumableFreeMemory = BaseNCodecTest.getPresumableFreeMemory();
334 
335         // Estimate the maximum memory required:
336         // 1GiB + 1GiB + ~2GiB + ~1.33GiB + 32 KiB  = ~5.33GiB
337         //
338         // 1GiB: Input buffer to encode
339         // 1GiB: Existing working buffer (due to doubling of default buffer size of 8192)
340         // ~2GiB: New working buffer to allocate (due to doubling)
341         // ~1.33GiB: Expected output size (since the working buffer is copied at the end)
342         // 32KiB: Some headroom
343         final long estimatedMemory = (long) size1GiB * 4 + expectedLength + 32 * 1024;
344         Assumptions.assumeTrue(presumableFreeMemory > estimatedMemory,
345                 "Not enough free memory for the test");
346 
347         final byte[] bytes = new byte[size1GiB];
348         final byte[] encoded = Base64.encodeBase64(bytes);
349         assertEquals(expectedLength, encoded.length);
350     }
351 
352     /**
353      * CODEC-68: isBase64 throws ArrayIndexOutOfBoundsException on some
354      * non-BASE64 bytes
355      */
356     @Test
357     void testCodec68() {
358         final byte[] x = { 'n', 'A', '=', '=', (byte) 0x9c };
359         Base64.decodeBase64(x);
360     }
361 
362     @Test
363     void testCodeInteger1() {
364         final String encodedInt1 = "li7dzDacuo67Jg7mtqEm2TRuOMU=";
365         final BigInteger bigInt1 = new BigInteger("857393771208094202104259627990318636601332086981");
366 
367         assertEquals(encodedInt1, new String(Base64.encodeInteger(bigInt1)));
368         assertEquals(bigInt1, Base64.decodeInteger(encodedInt1.getBytes(CHARSET_UTF8)));
369     }
370 
371     @Test
372     void testCodeInteger2() {
373         final String encodedInt2 = "9B5ypLY9pMOmtxCeTDHgwdNFeGs=";
374         final BigInteger bigInt2 = new BigInteger("1393672757286116725466646726891466679477132949611");
375 
376         assertEquals(encodedInt2, new String(Base64.encodeInteger(bigInt2)));
377         assertEquals(bigInt2, Base64.decodeInteger(encodedInt2.getBytes(CHARSET_UTF8)));
378     }
379 
380     @Test
381     void testCodeInteger3() {
382         final String encodedInt3 = "FKIhdgaG5LGKiEtF1vHy4f3y700zaD6QwDS3IrNVGzNp2" +
383             "rY+1LFWTK6D44AyiC1n8uWz1itkYMZF0/aKDK0Yjg==";
384         final BigInteger bigInt3 = new BigInteger(
385             "10806548154093873461951748545" +
386             "1196989136416448805819079363524309897749044958112417136240557" +
387             "4495062430572478766856090958495998158114332651671116876320938126");
388 
389         assertEquals(encodedInt3, new String(Base64.encodeInteger(bigInt3)));
390         assertEquals(bigInt3, Base64.decodeInteger(encodedInt3.getBytes(CHARSET_UTF8)));
391     }
392 
393     @Test
394     void testCodeInteger4() {
395         final String encodedInt4 = "ctA8YGxrtngg/zKVvqEOefnwmViFztcnPBYPlJsvh6yKI" +
396             "4iDm68fnp4Mi3RrJ6bZAygFrUIQLxLjV+OJtgJAEto0xAs+Mehuq1DkSFEpP3o" +
397             "DzCTOsrOiS1DwQe4oIb7zVk/9l7aPtJMHW0LVlMdwZNFNNJoqMcT2ZfCPrfvYv" +
398             "Q0=";
399         final BigInteger bigInt4 = new BigInteger("80624726256040348115552042320" +
400             "6968135001872753709424419772586693950232350200555646471175944" +
401             "519297087885987040810778908507262272892702303774422853675597" +
402             "748008534040890923814202286633163248086055216976551456088015" +
403             "338880713818192088877057717530169381044092839402438015097654" +
404             "53542091716518238707344493641683483917");
405 
406         assertEquals(encodedInt4, new String(Base64.encodeInteger(bigInt4)));
407         assertEquals(bigInt4, Base64.decodeInteger(encodedInt4.getBytes(CHARSET_UTF8)));
408     }
409 
410     @Test
411     void testCodeIntegerEdgeCases() {
412         // TODO
413     }
414 
415     @Test
416     void testCodeIntegerNull() {
417         assertThrows(NullPointerException.class, () -> Base64.encodeInteger(null),
418                 "Exception not thrown when passing in null to encodeInteger(BigInteger)");
419     }
420 
421     @Test
422     void testConstructor_Int_ByteArray_Boolean() {
423         final Base64 base64 = new Base64(65, new byte[] { '\t' }, false);
424         final byte[] encoded = base64.encode(BaseNTestData.DECODED);
425         String expectedResult = Base64TestData.ENCODED_64_CHARS_PER_LINE;
426         expectedResult = expectedResult.replace('\n', '\t');
427         final String result = StringUtils.newStringUtf8(encoded);
428         assertEquals(expectedResult, result, "new Base64(65, \\t, false)");
429     }
430 
431     @Test
432     void testConstructor_Int_ByteArray_Boolean_UrlSafe() {
433         // url-safe variation
434         final Base64 base64 = new Base64(64, new byte[] { '\t' }, true);
435         final byte[] encoded = base64.encode(BaseNTestData.DECODED);
436         String expectedResult = Base64TestData.ENCODED_64_CHARS_PER_LINE;
437         expectedResult = expectedResult.replace("=", ""); // url-safe has no
438                                                                 // == padding.
439         expectedResult = expectedResult.replace('\n', '\t');
440         expectedResult = expectedResult.replace('+', '-');
441         expectedResult = expectedResult.replace('/', '_');
442         final String result = StringUtils.newStringUtf8(encoded);
443         assertEquals(result, expectedResult, "new Base64(64, \\t, true)");
444     }
445 
446     @Test
447     void testConstructors() {
448         Base64 base64;
449         base64 = new Base64();
450         base64 = new Base64(-1);
451         base64 = new Base64(-1, new byte[] {});
452         base64 = new Base64(64, new byte[] {});
453         base64 = new Base64(64, new byte[] {'$'}); // OK
454 
455         assertThrows(IllegalArgumentException.class, () -> new Base64(-1, new byte[] { 'A' }),
456                 "Should have rejected attempt to use 'A' as a line separator");
457         // TODO do we need to check sep if len = -1?
458 
459         assertThrows(IllegalArgumentException.class, () -> new Base64(64, new byte[] { 'A' }),
460                 "Should have rejected attempt to use 'A' as a line separator");
461 
462         assertThrows(IllegalArgumentException.class, () -> new Base64(64, new byte[] { '=' }),
463                 "Should have rejected attempt to use '=' as a line separator");
464 
465         base64 = new Base64(64, new byte[] { '$' }); // OK
466 
467         assertThrows(IllegalArgumentException.class, () -> new Base64(64, new byte[] { 'A', '$' }),
468                 "Should have rejected attempt to use 'A$' as a line separator");
469 
470         base64 = new Base64(64, new byte[] { ' ', '$', '\n', '\r', '\t' }); // OK
471 
472         assertNotNull(base64);
473     }
474 
475     @Test
476     void testCustomEncodingAlphabet() {
477         // created a duplicate of STANDARD_ENCODE_TABLE and replaced two chars with
478         // custom values not already present in table
479         // A => . B => -
480         // @formatter:off
481         final byte[] encodeTable = {
482                 '.', '-', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
483                 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
484                 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
485                 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
486                 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
487         };
488         // @formatter:on
489 
490         // two instances: one with default table and one with adjusted encoding table
491         final Base64 b64 = new Base64();
492         final Base64 b64customEncoding = Base64.builder().setEncodeTable(encodeTable).get();
493 
494         final String content = "! Hello World - this §$%";
495 
496         final byte[] encodedBytes = b64.encode(StringUtils.getBytesUtf8(content));
497         final String encodedContent = StringUtils.newStringUtf8(encodedBytes);
498 
499         final byte[] encodedBytesCustom = b64customEncoding.encode(StringUtils.getBytesUtf8(content));
500         final String encodedContentCustom = StringUtils.newStringUtf8(encodedBytesCustom);
501 
502         assertTrue(encodedContent.contains("A") && encodedContent.contains("B"),
503                 "testing precondition not met - ecodedContent should contain parts of modified table");
504 
505         assertEquals(encodedContent.replace('A', '.').replace('B', '-') // replace alphabet adjustments
506                 .replace("=", "") // remove padding (not default alphabet)
507                 , encodedContentCustom);
508 
509         // try decode encoded content
510         final byte[] decode = b64customEncoding.decode(encodedBytesCustom);
511         final String decodeString = StringUtils.newStringUtf8(decode);
512 
513         assertEquals(content, decodeString);
514     }
515 
516     @Test
517     void testCustomEncodingAlphabet_illegal() {
518         final byte[] encodeTable = {
519                 '.', '-', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M'
520         };
521         assertThrows(IllegalArgumentException.class, () -> Base64.builder().setEncodeTable(encodeTable).get());
522     }
523 
524     private void testDecodeEncode(final String encodedText) {
525         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64(encodedText));
526         final String encodedText2 = Base64.encodeBase64String(StringUtils.getBytesUtf8(decodedText));
527         assertEquals(encodedText, encodedText2);
528     }
529 
530     /**
531      * Tests conditional true branch for "marker0" test.
532      */
533     @Test
534     void testDecodePadMarkerIndex2() {
535         assertEquals("A", new String(Base64.decodeBase64("QQ==".getBytes(CHARSET_UTF8))));
536     }
537 
538     /**
539      * Tests conditional branches for "marker1" test.
540      */
541     @Test
542     void testDecodePadMarkerIndex3() {
543         assertEquals("AA", new String(Base64.decodeBase64("QUE=".getBytes(CHARSET_UTF8))));
544         assertEquals("AAA", new String(Base64.decodeBase64("QUFB".getBytes(CHARSET_UTF8))));
545     }
546 
547     @Test
548     void testDecodePadOnly() {
549         assertEquals(0, Base64.decodeBase64("====".getBytes(CHARSET_UTF8)).length);
550         assertEquals("", new String(Base64.decodeBase64("====".getBytes(CHARSET_UTF8))));
551         // Test truncated padding
552         assertEquals(0, Base64.decodeBase64("===".getBytes(CHARSET_UTF8)).length);
553         assertEquals(0, Base64.decodeBase64("==".getBytes(CHARSET_UTF8)).length);
554         assertEquals(0, Base64.decodeBase64("=".getBytes(CHARSET_UTF8)).length);
555         assertEquals(0, Base64.decodeBase64("".getBytes(CHARSET_UTF8)).length);
556     }
557 
558     @Test
559     void testDecodePadOnlyChunked() {
560         assertEquals(0, Base64.decodeBase64("====\n".getBytes(CHARSET_UTF8)).length);
561         assertEquals("", new String(Base64.decodeBase64("====\n".getBytes(CHARSET_UTF8))));
562         // Test truncated padding
563         assertEquals(0, Base64.decodeBase64("===\n".getBytes(CHARSET_UTF8)).length);
564         assertEquals(0, Base64.decodeBase64("==\n".getBytes(CHARSET_UTF8)).length);
565         assertEquals(0, Base64.decodeBase64("=\n".getBytes(CHARSET_UTF8)).length);
566         assertEquals(0, Base64.decodeBase64("\n".getBytes(CHARSET_UTF8)).length);
567     }
568 
569     /**
570      * Test our decode with pad character in the middle. (Our current
571      * implementation: halt decode and return what we've got so far).
572      *
573      * The point of this test is not to say
574      * "this is the correct way to decode base64." The point is simply to keep
575      * us aware of the current logic since 1.4 so we don't accidentally break it
576      * without realizing.
577      *
578      * Note for historians. The 1.3 logic would decode to:
579      * "Hello World\u0000Hello World" -- null in the middle --- and 1.4
580      * unwittingly changed it to current logic.
581      */
582     @Test
583     void testDecodeWithInnerPad() {
584         final String content = "SGVsbG8gV29ybGQ=SGVsbG8gV29ybGQ=";
585         final byte[] result = Base64.decodeBase64(content);
586         final byte[] shouldBe = StringUtils.getBytesUtf8("Hello World");
587         assertArrayEquals(result, shouldBe, "decode should halt at pad (=)");
588     }
589 
590     @Test
591     void testDecodeWithWhitespace() throws Exception {
592 
593         final String orig = "I am a late night coder.";
594 
595         final byte[] encodedArray = Base64.encodeBase64(orig.getBytes(CHARSET_UTF8));
596         final StringBuilder intermediate = new StringBuilder(new String(encodedArray));
597 
598         intermediate.insert(2, ' ');
599         intermediate.insert(5, '\t');
600         intermediate.insert(10, '\r');
601         intermediate.insert(15, '\n');
602 
603         final byte[] encodedWithWS = intermediate.toString().getBytes(CHARSET_UTF8);
604         final byte[] decodedWithWS = Base64.decodeBase64(encodedWithWS);
605 
606         final String dest = new String(decodedWithWS);
607 
608         assertEquals(orig, dest, "Dest string doesn't equal the original");
609     }
610 
611     /**
612      * Test encode and decode of empty byte array.
613      */
614     @Test
615     void testEmptyBase64() {
616         byte[] empty = {};
617         byte[] result = Base64.encodeBase64(empty);
618         assertEquals(0, result.length, "empty base64 encode");
619         assertNull(Base64.encodeBase64(null), "empty base64 encode");
620         result = new Base64().encode(empty, 0, 1);
621         assertEquals(0, result.length, "empty base64 encode");
622         assertNull(new Base64().encode(null, 0, 1), "empty base64 encode");
623 
624         empty = new byte[0];
625         result = Base64.decodeBase64(empty);
626         assertEquals(0, result.length, "empty base64 decode");
627         assertNull(Base64.decodeBase64((byte[]) null), "empty base64 encode");
628     }
629 
630     private void testEncodeDecode(final String plainText) {
631         final String encodedText = Base64.encodeBase64String(StringUtils.getBytesUtf8(plainText));
632         final String decodedText = StringUtils.newStringUsAscii(Base64.decodeBase64(encodedText));
633         assertEquals(plainText, decodedText);
634     }
635 
636     // encode/decode a large random array
637     @Test
638     void testEncodeDecodeRandom() {
639         for (int i = 1; i < 5; i++) {
640             final byte[] data = new byte[getRandom().nextInt(10000) + 1];
641             getRandom().nextBytes(data);
642             final byte[] enc = Base64.encodeBase64(data);
643             assertTrue(Base64.isBase64(enc));
644             final byte[] data2 = Base64.decodeBase64(enc);
645             assertArrayEquals(data, data2);
646         }
647     }
648 
649     // encode/decode random arrays from size 0 to size 11
650     @Test
651     void testEncodeDecodeSmall() {
652         for (int i = 0; i < 12; i++) {
653             final byte[] data = new byte[i];
654             getRandom().nextBytes(data);
655             final byte[] enc = Base64.encodeBase64(data);
656             assertTrue(Base64.isBase64(enc), "\"" + new String(enc) + "\" is Base64 data.");
657             final byte[] data2 = Base64.decodeBase64(enc);
658             assertArrayEquals(data, data2, toString(data) + " equals " + toString(data2));
659         }
660     }
661 
662     @Test
663     void testEncodeOverMaxSize() throws Exception {
664         testEncodeOverMaxSize(-1);
665         testEncodeOverMaxSize(0);
666         testEncodeOverMaxSize(1);
667         testEncodeOverMaxSize(2);
668     }
669 
670     private void testEncodeOverMaxSize(final int maxSize) {
671         assertThrows(IllegalArgumentException.class, () -> Base64.encodeBase64(BaseNTestData.DECODED, true, false, maxSize));
672     }
673 
674     /**
675      * Tests a lineSeparator much bigger than DEFAULT_BUFFER_SIZE.
676      *
677      * @see "<a href='https://mail-archives.apache.org/mod_mbox/commons-dev/201202.mbox/%3C4F3C85D7.5060706@snafu.de%3E'>dev@commons.apache.org</a>"
678      */
679     @Test
680     void testHugeLineSeparator() {
681         final int BaseNCodec_DEFAULT_BUFFER_SIZE = 8192;
682         final int Base64_BYTES_PER_ENCODED_BLOCK = 4;
683         final byte[] baLineSeparator = new byte[BaseNCodec_DEFAULT_BUFFER_SIZE * 4 - 3];
684         final Base64 b64 = new Base64(Base64_BYTES_PER_ENCODED_BLOCK, baLineSeparator);
685         final String strOriginal = "Hello World";
686         final String strDecoded = new String(b64.decode(b64.encode(StringUtils.getBytesUtf8(strOriginal))));
687         assertEquals(strOriginal, strDecoded, "testDEFAULT_BUFFER_SIZE");
688     }
689 
690     @Test
691     void testIgnoringNonBase64InDecode() throws Exception {
692         assertEquals(FOX_TEXT, new String(Base64.decodeBase64(FOX_BASE64.getBytes(CHARSET_UTF8))));
693     }
694 
695     @Test
696     void testIsArrayByteBase64() {
697         assertFalse(Base64.isBase64(new byte[] { Byte.MIN_VALUE }));
698         assertFalse(Base64.isBase64(new byte[] { -125 }));
699         assertFalse(Base64.isBase64(new byte[] { -10 }));
700         assertFalse(Base64.isBase64(new byte[] { 0 }));
701         assertFalse(Base64.isBase64(new byte[] { 64, Byte.MAX_VALUE }));
702         assertFalse(Base64.isBase64(new byte[] { Byte.MAX_VALUE }));
703 
704         assertTrue(Base64.isBase64(new byte[] { 'A' }));
705 
706         assertFalse(Base64.isBase64(new byte[] { 'A', Byte.MIN_VALUE }));
707 
708         assertTrue(Base64.isBase64(new byte[] { 'A', 'Z', 'a' }));
709         assertTrue(Base64.isBase64(new byte[] { '/', '=', '+' }));
710 
711         assertFalse(Base64.isBase64(new byte[] { '$' }));
712     }
713 
714     /**
715      * Test the isStringBase64 method.
716      */
717     @Test
718     void testIsStringBase64() {
719         final String nullString = null;
720         final String emptyString = "";
721         final String validString = "abc===defg\n\r123456\r789\r\rABC\n\nDEF==GHI\r\nJKL==============";
722         final String invalidString = validString + (char) 0; // append null character
723 
724         assertThrows(NullPointerException.class, () -> Base64.isBase64(nullString),
725                 "Base64.isStringBase64() should not be null-safe.");
726 
727         assertTrue(Base64.isBase64(emptyString), "Base64.isStringBase64(empty-string) is true");
728         assertTrue(Base64.isBase64(validString), "Base64.isStringBase64(valid-string) is true");
729         assertFalse(Base64.isBase64(invalidString), "Base64.isStringBase64(invalid-string) is false");
730     }
731 
732     /**
733      * Tests isUrlSafe.
734      */
735     @Test
736     void testIsUrlSafe() {
737         final Base64 base64Standard = new Base64(false);
738         final Base64 base64URLSafe = new Base64(true);
739 
740         assertFalse(base64Standard.isUrlSafe(), "Base64.isUrlSafe=false");
741         assertTrue(base64URLSafe.isUrlSafe(), "Base64.isUrlSafe=true");
742 
743         final byte[] whiteSpace = { ' ', '\n', '\r', '\t' };
744         assertTrue(Base64.isBase64(whiteSpace), "Base64.isBase64(whiteSpace)=true");
745     }
746 
747     @Test
748     void testKnownDecodings() {
749         assertEquals(FOX_TEXT, new String(Base64.decodeBase64(
750                 "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2dzLg==".getBytes(CHARSET_UTF8))));
751         assertEquals("It was the best of times, it was the worst of times.", new String(Base64.decodeBase64(
752                 "SXQgd2FzIHRoZSBiZXN0IG9mIHRpbWVzLCBpdCB3YXMgdGhlIHdvcnN0IG9mIHRpbWVzLg==".getBytes(CHARSET_UTF8))));
753         assertEquals("http://jakarta.apache.org/commmons", new String(
754                 Base64.decodeBase64("aHR0cDovL2pha2FydGEuYXBhY2hlLm9yZy9jb21tbW9ucw==".getBytes(CHARSET_UTF8))));
755         assertEquals("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz", new String(Base64.decodeBase64(
756                 "QWFCYkNjRGRFZUZmR2dIaElpSmpLa0xsTW1Obk9vUHBRcVJyU3NUdFV1VnZXd1h4WXlaeg==".getBytes(CHARSET_UTF8))));
757         assertEquals("{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }",
758                 new String(Base64.decodeBase64("eyAwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5IH0=".getBytes(CHARSET_UTF8))));
759         assertEquals("xyzzy!", new String(Base64.decodeBase64("eHl6enkh".getBytes(CHARSET_UTF8))));
760     }
761 
762     @Test
763     void testKnownEncodings() {
764         assertEquals("VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wZWQgb3ZlciB0aGUgbGF6eSBkb2dzLg==", new String(
765                 Base64.encodeBase64(FOX_TEXT.getBytes(CHARSET_UTF8))));
766         assertEquals(
767                 "YmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJs\r\nYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFo\r\nIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBi\r\nbGFoIGJsYWg=\r\n",
768                 new String(Base64.encodeBase64Chunked(
769                         "blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah"
770                                 .getBytes(CHARSET_UTF8))));
771         assertEquals("SXQgd2FzIHRoZSBiZXN0IG9mIHRpbWVzLCBpdCB3YXMgdGhlIHdvcnN0IG9mIHRpbWVzLg==", new String(
772                 Base64.encodeBase64("It was the best of times, it was the worst of times.".getBytes(CHARSET_UTF8))));
773         assertEquals("aHR0cDovL2pha2FydGEuYXBhY2hlLm9yZy9jb21tbW9ucw==",
774                 new String(Base64.encodeBase64("http://jakarta.apache.org/commmons".getBytes(CHARSET_UTF8))));
775         assertEquals("QWFCYkNjRGRFZUZmR2dIaElpSmpLa0xsTW1Obk9vUHBRcVJyU3NUdFV1VnZXd1h4WXlaeg==", new String(
776                 Base64.encodeBase64("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz".getBytes(CHARSET_UTF8))));
777         assertEquals("eyAwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4LCA5IH0=",
778                 new String(Base64.encodeBase64("{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }".getBytes(CHARSET_UTF8))));
779         assertEquals("eHl6enkh", new String(Base64.encodeBase64("xyzzy!".getBytes(CHARSET_UTF8))));
780     }
781 
782     @Test
783     void testNonBase64Test() throws Exception {
784 
785         final byte[] bArray = { '%' };
786 
787         assertFalse(Base64.isBase64(bArray),
788                 "Invalid Base64 array was incorrectly validated as an array of Base64 encoded data");
789 
790         try {
791             final Base64 b64 = new Base64();
792             final byte[] result = b64.decode(bArray);
793 
794             assertEquals(0, result.length, "The result should be empty as the test encoded content did " +
795                     "not contain any valid base 64 characters");
796         } catch (final Exception e) {
797             fail("Exception '" + e.getClass().getName() + "' was thrown when trying to decode " +
798                 "invalid base64 encoded data - RFC 2045 requires that all " +
799                 "non base64 character be discarded, an exception should not have been thrown");
800         }
801     }
802 
803     @Test
804     void testObjectDecodeWithInvalidParameter() {
805         assertThrows(DecoderException.class, () -> new Base64().decode(Integer.valueOf(5)),
806             "decode(Object) didn't throw an exception when passed an Integer object");
807     }
808 
809     @Test
810     void testObjectDecodeWithValidParameter() throws Exception {
811 
812         final String original = "Hello World!";
813         final Object o = Base64.encodeBase64(original.getBytes(CHARSET_UTF8));
814 
815         final Base64 b64 = new Base64();
816         final Object oDecoded = b64.decode(o);
817         final byte[] baDecoded = (byte[]) oDecoded;
818         final String dest = new String(baDecoded);
819 
820         assertEquals(original, dest, "dest string does not equal original");
821     }
822 
823     @Test
824     void testObjectEncode() throws Exception {
825         final Base64 b64 = new Base64();
826         assertEquals("SGVsbG8gV29ybGQ=", new String(b64.encode("Hello World".getBytes(CHARSET_UTF8))));
827     }
828 
829     @Test
830     void testObjectEncodeWithInvalidParameter() {
831         assertThrows(EncoderException.class, () -> new Base64().encode("Yadayadayada"),
832                 "encode(Object) didn't throw an exception when passed a String object");
833     }
834 
835     @Test
836     void testObjectEncodeWithValidParameter() throws Exception {
837 
838         final String original = "Hello World!";
839         final Object origObj = original.getBytes(CHARSET_UTF8);
840 
841         final Base64 b64 = new Base64();
842         final Object oEncoded = b64.encode(origObj);
843         final byte[] bArray = Base64.decodeBase64((byte[]) oEncoded);
844         final String dest = new String(bArray);
845 
846         assertEquals(original, dest, "dest string does not equal original");
847     }
848 
849     @Test
850     void testPairs() {
851         assertEquals("AAA=", new String(Base64.encodeBase64(new byte[] { 0, 0 })));
852         for (int i = -128; i <= 127; i++) {
853             final byte[] test = { (byte) i, (byte) i };
854             assertArrayEquals(test, Base64.decodeBase64(Base64.encodeBase64(test)));
855         }
856     }
857 
858     /**
859      * Tests RFC 1421 section 4.3.2.4 chuck size definition.
860      */
861     @Test
862     void testRfc1421Section6Dot8ChunkSizeDefinition() {
863         assertEquals(64, BaseNCodec.PEM_CHUNK_SIZE);
864     }
865 
866     /**
867      * Tests RFC 2045 section 2.1 CRLF definition.
868      */
869     @Test
870     void testRfc2045Section2Dot1CrLfDefinition() {
871         assertArrayEquals(new byte[]{13, 10}, BaseNCodec.CHUNK_SEPARATOR);
872     }
873 
874     /**
875      * Tests RFC 2045 section 6.8 chuck size definition.
876      */
877     @Test
878     void testRfc2045Section6Dot8ChunkSizeDefinition() {
879         assertEquals(76, BaseNCodec.MIME_CHUNK_SIZE);
880     }
881 
882     /**
883      * Tests RFC 4648 section 10 test vectors.
884      * <ul>
885      * <li>BASE64("") = ""</li>
886      * <li>BASE64("f") = "Zg=="</li>
887      * <li>BASE64("fo") = "Zm8="</li>
888      * <li>BASE64("foo") = "Zm9v"</li>
889      * <li>BASE64("foob") = "Zm9vYg=="</li>
890      * <li>BASE64("fooba") = "Zm9vYmE="</li>
891      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
892      * </ul>
893      *
894      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
895      *      html/rfc4648</a>
896      */
897     @Test
898     void testRfc4648Section10Decode() {
899         assertEquals("", StringUtils.newStringUsAscii(Base64.decodeBase64("")));
900         assertEquals("f", StringUtils.newStringUsAscii(Base64.decodeBase64("Zg==")));
901         assertEquals("fo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm8=")));
902         assertEquals("foo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9v")));
903         assertEquals("foob", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYg==")));
904         assertEquals("fooba", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmE=")));
905         assertEquals("foobar", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmFy")));
906     }
907 
908     /**
909      * Tests RFC 4648 section 10 test vectors.
910      * <ul>
911      * <li>BASE64("") = ""</li>
912      * <li>BASE64("f") = "Zg=="</li>
913      * <li>BASE64("fo") = "Zm8="</li>
914      * <li>BASE64("foo") = "Zm9v"</li>
915      * <li>BASE64("foob") = "Zm9vYg=="</li>
916      * <li>BASE64("fooba") = "Zm9vYmE="</li>
917      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
918      * </ul>
919      *
920      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
921      *      html/rfc4648</a>
922      */
923     @Test
924     void testRfc4648Section10DecodeEncode() {
925         testDecodeEncode("");
926         testDecodeEncode("Zg==");
927         testDecodeEncode("Zm8=");
928         testDecodeEncode("Zm9v");
929         testDecodeEncode("Zm9vYg==");
930         testDecodeEncode("Zm9vYmE=");
931         testDecodeEncode("Zm9vYmFy");
932     }
933 
934     /**
935      * Tests RFC 4648 section 10 test vectors.
936      * <ul>
937      * <li>BASE64("") = ""</li>
938      * <li>BASE64("f") = "Zg=="</li>
939      * <li>BASE64("fo") = "Zm8="</li>
940      * <li>BASE64("foo") = "Zm9v"</li>
941      * <li>BASE64("foob") = "Zm9vYg=="</li>
942      * <li>BASE64("fooba") = "Zm9vYmE="</li>
943      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
944      * </ul>
945      *
946      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
947      *      html/rfc4648</a>
948      */
949     @Test
950     void testRfc4648Section10DecodeWithCrLf() {
951         final String CRLF = StringUtils.newStringUsAscii(BaseNCodec.CHUNK_SEPARATOR);
952         assertEquals("", StringUtils.newStringUsAscii(Base64.decodeBase64("" + CRLF)));
953         assertEquals("f", StringUtils.newStringUsAscii(Base64.decodeBase64("Zg==" + CRLF)));
954         assertEquals("fo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm8=" + CRLF)));
955         assertEquals("foo", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9v" + CRLF)));
956         assertEquals("foob", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYg==" + CRLF)));
957         assertEquals("fooba", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmE=" + CRLF)));
958         assertEquals("foobar", StringUtils.newStringUsAscii(Base64.decodeBase64("Zm9vYmFy" + CRLF)));
959     }
960 
961     /**
962      * Tests RFC 4648 section 10 test vectors.
963      * <ul>
964      * <li>BASE64("") = ""</li>
965      * <li>BASE64("f") = "Zg=="</li>
966      * <li>BASE64("fo") = "Zm8="</li>
967      * <li>BASE64("foo") = "Zm9v"</li>
968      * <li>BASE64("foob") = "Zm9vYg=="</li>
969      * <li>BASE64("fooba") = "Zm9vYmE="</li>
970      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
971      * </ul>
972      *
973      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
974      *      html/rfc4648</a>
975      */
976     @Test
977     void testRfc4648Section10Encode() {
978         assertEquals("", Base64.encodeBase64String(StringUtils.getBytesUtf8("")));
979         assertEquals("Zg==", Base64.encodeBase64String(StringUtils.getBytesUtf8("f")));
980         assertEquals("Zm8=", Base64.encodeBase64String(StringUtils.getBytesUtf8("fo")));
981         assertEquals("Zm9v", Base64.encodeBase64String(StringUtils.getBytesUtf8("foo")));
982         assertEquals("Zm9vYg==", Base64.encodeBase64String(StringUtils.getBytesUtf8("foob")));
983         assertEquals("Zm9vYmE=", Base64.encodeBase64String(StringUtils.getBytesUtf8("fooba")));
984         assertEquals("Zm9vYmFy", Base64.encodeBase64String(StringUtils.getBytesUtf8("foobar")));
985     }
986 
987     /**
988      * Tests RFC 4648 section 10 test vectors.
989      * <ul>
990      * <li>BASE64("") = ""</li>
991      * <li>BASE64("f") = "Zg=="</li>
992      * <li>BASE64("fo") = "Zm8="</li>
993      * <li>BASE64("foo") = "Zm9v"</li>
994      * <li>BASE64("foob") = "Zm9vYg=="</li>
995      * <li>BASE64("fooba") = "Zm9vYmE="</li>
996      * <li>BASE64("foobar") = "Zm9vYmFy"</li>
997      * </ul>
998      *
999      * @see <a href="https://tools.ietf.org/html/rfc4648">https://tools.ietf.org/
1000      *      html/rfc4648</a>
1001      */
1002     @Test
1003     void testRfc4648Section10EncodeDecode() {
1004         testEncodeDecode("");
1005         testEncodeDecode("f");
1006         testEncodeDecode("fo");
1007         testEncodeDecode("foo");
1008         testEncodeDecode("foob");
1009         testEncodeDecode("fooba");
1010         testEncodeDecode("foobar");
1011     }
1012 
1013     @Test
1014     void testSingletons() {
1015         assertEquals("AA==", new String(Base64.encodeBase64(new byte[] { (byte) 0 })));
1016         assertEquals("AQ==", new String(Base64.encodeBase64(new byte[] { (byte) 1 })));
1017         assertEquals("Ag==", new String(Base64.encodeBase64(new byte[] { (byte) 2 })));
1018         assertEquals("Aw==", new String(Base64.encodeBase64(new byte[] { (byte) 3 })));
1019         assertEquals("BA==", new String(Base64.encodeBase64(new byte[] { (byte) 4 })));
1020         assertEquals("BQ==", new String(Base64.encodeBase64(new byte[] { (byte) 5 })));
1021         assertEquals("Bg==", new String(Base64.encodeBase64(new byte[] { (byte) 6 })));
1022         assertEquals("Bw==", new String(Base64.encodeBase64(new byte[] { (byte) 7 })));
1023         assertEquals("CA==", new String(Base64.encodeBase64(new byte[] { (byte) 8 })));
1024         assertEquals("CQ==", new String(Base64.encodeBase64(new byte[] { (byte) 9 })));
1025         assertEquals("Cg==", new String(Base64.encodeBase64(new byte[] { (byte) 10 })));
1026         assertEquals("Cw==", new String(Base64.encodeBase64(new byte[] { (byte) 11 })));
1027         assertEquals("DA==", new String(Base64.encodeBase64(new byte[] { (byte) 12 })));
1028         assertEquals("DQ==", new String(Base64.encodeBase64(new byte[] { (byte) 13 })));
1029         assertEquals("Dg==", new String(Base64.encodeBase64(new byte[] { (byte) 14 })));
1030         assertEquals("Dw==", new String(Base64.encodeBase64(new byte[] { (byte) 15 })));
1031         assertEquals("EA==", new String(Base64.encodeBase64(new byte[] { (byte) 16 })));
1032         assertEquals("EQ==", new String(Base64.encodeBase64(new byte[] { (byte) 17 })));
1033         assertEquals("Eg==", new String(Base64.encodeBase64(new byte[] { (byte) 18 })));
1034         assertEquals("Ew==", new String(Base64.encodeBase64(new byte[] { (byte) 19 })));
1035         assertEquals("FA==", new String(Base64.encodeBase64(new byte[] { (byte) 20 })));
1036         assertEquals("FQ==", new String(Base64.encodeBase64(new byte[] { (byte) 21 })));
1037         assertEquals("Fg==", new String(Base64.encodeBase64(new byte[] { (byte) 22 })));
1038         assertEquals("Fw==", new String(Base64.encodeBase64(new byte[] { (byte) 23 })));
1039         assertEquals("GA==", new String(Base64.encodeBase64(new byte[] { (byte) 24 })));
1040         assertEquals("GQ==", new String(Base64.encodeBase64(new byte[] { (byte) 25 })));
1041         assertEquals("Gg==", new String(Base64.encodeBase64(new byte[] { (byte) 26 })));
1042         assertEquals("Gw==", new String(Base64.encodeBase64(new byte[] { (byte) 27 })));
1043         assertEquals("HA==", new String(Base64.encodeBase64(new byte[] { (byte) 28 })));
1044         assertEquals("HQ==", new String(Base64.encodeBase64(new byte[] { (byte) 29 })));
1045         assertEquals("Hg==", new String(Base64.encodeBase64(new byte[] { (byte) 30 })));
1046         assertEquals("Hw==", new String(Base64.encodeBase64(new byte[] { (byte) 31 })));
1047         assertEquals("IA==", new String(Base64.encodeBase64(new byte[] { (byte) 32 })));
1048         assertEquals("IQ==", new String(Base64.encodeBase64(new byte[] { (byte) 33 })));
1049         assertEquals("Ig==", new String(Base64.encodeBase64(new byte[] { (byte) 34 })));
1050         assertEquals("Iw==", new String(Base64.encodeBase64(new byte[] { (byte) 35 })));
1051         assertEquals("JA==", new String(Base64.encodeBase64(new byte[] { (byte) 36 })));
1052         assertEquals("JQ==", new String(Base64.encodeBase64(new byte[] { (byte) 37 })));
1053         assertEquals("Jg==", new String(Base64.encodeBase64(new byte[] { (byte) 38 })));
1054         assertEquals("Jw==", new String(Base64.encodeBase64(new byte[] { (byte) 39 })));
1055         assertEquals("KA==", new String(Base64.encodeBase64(new byte[] { (byte) 40 })));
1056         assertEquals("KQ==", new String(Base64.encodeBase64(new byte[] { (byte) 41 })));
1057         assertEquals("Kg==", new String(Base64.encodeBase64(new byte[] { (byte) 42 })));
1058         assertEquals("Kw==", new String(Base64.encodeBase64(new byte[] { (byte) 43 })));
1059         assertEquals("LA==", new String(Base64.encodeBase64(new byte[] { (byte) 44 })));
1060         assertEquals("LQ==", new String(Base64.encodeBase64(new byte[] { (byte) 45 })));
1061         assertEquals("Lg==", new String(Base64.encodeBase64(new byte[] { (byte) 46 })));
1062         assertEquals("Lw==", new String(Base64.encodeBase64(new byte[] { (byte) 47 })));
1063         assertEquals("MA==", new String(Base64.encodeBase64(new byte[] { (byte) 48 })));
1064         assertEquals("MQ==", new String(Base64.encodeBase64(new byte[] { (byte) 49 })));
1065         assertEquals("Mg==", new String(Base64.encodeBase64(new byte[] { (byte) 50 })));
1066         assertEquals("Mw==", new String(Base64.encodeBase64(new byte[] { (byte) 51 })));
1067         assertEquals("NA==", new String(Base64.encodeBase64(new byte[] { (byte) 52 })));
1068         assertEquals("NQ==", new String(Base64.encodeBase64(new byte[] { (byte) 53 })));
1069         assertEquals("Ng==", new String(Base64.encodeBase64(new byte[] { (byte) 54 })));
1070         assertEquals("Nw==", new String(Base64.encodeBase64(new byte[] { (byte) 55 })));
1071         assertEquals("OA==", new String(Base64.encodeBase64(new byte[] { (byte) 56 })));
1072         assertEquals("OQ==", new String(Base64.encodeBase64(new byte[] { (byte) 57 })));
1073         assertEquals("Og==", new String(Base64.encodeBase64(new byte[] { (byte) 58 })));
1074         assertEquals("Ow==", new String(Base64.encodeBase64(new byte[] { (byte) 59 })));
1075         assertEquals("PA==", new String(Base64.encodeBase64(new byte[] { (byte) 60 })));
1076         assertEquals("PQ==", new String(Base64.encodeBase64(new byte[] { (byte) 61 })));
1077         assertEquals("Pg==", new String(Base64.encodeBase64(new byte[] { (byte) 62 })));
1078         assertEquals("Pw==", new String(Base64.encodeBase64(new byte[] { (byte) 63 })));
1079         assertEquals("QA==", new String(Base64.encodeBase64(new byte[] { (byte) 64 })));
1080         assertEquals("QQ==", new String(Base64.encodeBase64(new byte[] { (byte) 65 })));
1081         assertEquals("Qg==", new String(Base64.encodeBase64(new byte[] { (byte) 66 })));
1082         assertEquals("Qw==", new String(Base64.encodeBase64(new byte[] { (byte) 67 })));
1083         assertEquals("RA==", new String(Base64.encodeBase64(new byte[] { (byte) 68 })));
1084         assertEquals("RQ==", new String(Base64.encodeBase64(new byte[] { (byte) 69 })));
1085         assertEquals("Rg==", new String(Base64.encodeBase64(new byte[] { (byte) 70 })));
1086         assertEquals("Rw==", new String(Base64.encodeBase64(new byte[] { (byte) 71 })));
1087         assertEquals("SA==", new String(Base64.encodeBase64(new byte[] { (byte) 72 })));
1088         assertEquals("SQ==", new String(Base64.encodeBase64(new byte[] { (byte) 73 })));
1089         assertEquals("Sg==", new String(Base64.encodeBase64(new byte[] { (byte) 74 })));
1090         assertEquals("Sw==", new String(Base64.encodeBase64(new byte[] { (byte) 75 })));
1091         assertEquals("TA==", new String(Base64.encodeBase64(new byte[] { (byte) 76 })));
1092         assertEquals("TQ==", new String(Base64.encodeBase64(new byte[] { (byte) 77 })));
1093         assertEquals("Tg==", new String(Base64.encodeBase64(new byte[] { (byte) 78 })));
1094         assertEquals("Tw==", new String(Base64.encodeBase64(new byte[] { (byte) 79 })));
1095         assertEquals("UA==", new String(Base64.encodeBase64(new byte[] { (byte) 80 })));
1096         assertEquals("UQ==", new String(Base64.encodeBase64(new byte[] { (byte) 81 })));
1097         assertEquals("Ug==", new String(Base64.encodeBase64(new byte[] { (byte) 82 })));
1098         assertEquals("Uw==", new String(Base64.encodeBase64(new byte[] { (byte) 83 })));
1099         assertEquals("VA==", new String(Base64.encodeBase64(new byte[] { (byte) 84 })));
1100         assertEquals("VQ==", new String(Base64.encodeBase64(new byte[] { (byte) 85 })));
1101         assertEquals("Vg==", new String(Base64.encodeBase64(new byte[] { (byte) 86 })));
1102         assertEquals("Vw==", new String(Base64.encodeBase64(new byte[] { (byte) 87 })));
1103         assertEquals("WA==", new String(Base64.encodeBase64(new byte[] { (byte) 88 })));
1104         assertEquals("WQ==", new String(Base64.encodeBase64(new byte[] { (byte) 89 })));
1105         assertEquals("Wg==", new String(Base64.encodeBase64(new byte[] { (byte) 90 })));
1106         assertEquals("Ww==", new String(Base64.encodeBase64(new byte[] { (byte) 91 })));
1107         assertEquals("XA==", new String(Base64.encodeBase64(new byte[] { (byte) 92 })));
1108         assertEquals("XQ==", new String(Base64.encodeBase64(new byte[] { (byte) 93 })));
1109         assertEquals("Xg==", new String(Base64.encodeBase64(new byte[] { (byte) 94 })));
1110         assertEquals("Xw==", new String(Base64.encodeBase64(new byte[] { (byte) 95 })));
1111         assertEquals("YA==", new String(Base64.encodeBase64(new byte[] { (byte) 96 })));
1112         assertEquals("YQ==", new String(Base64.encodeBase64(new byte[] { (byte) 97 })));
1113         assertEquals("Yg==", new String(Base64.encodeBase64(new byte[] { (byte) 98 })));
1114         assertEquals("Yw==", new String(Base64.encodeBase64(new byte[] { (byte) 99 })));
1115         assertEquals("ZA==", new String(Base64.encodeBase64(new byte[] { (byte) 100 })));
1116         assertEquals("ZQ==", new String(Base64.encodeBase64(new byte[] { (byte) 101 })));
1117         assertEquals("Zg==", new String(Base64.encodeBase64(new byte[] { (byte) 102 })));
1118         assertEquals("Zw==", new String(Base64.encodeBase64(new byte[] { (byte) 103 })));
1119         assertEquals("aA==", new String(Base64.encodeBase64(new byte[] { (byte) 104 })));
1120         for (int i = -128; i <= 127; i++) {
1121             final byte[] test = { (byte) i };
1122             assertArrayEquals(test, Base64.decodeBase64(Base64.encodeBase64(test)));
1123         }
1124     }
1125 
1126     @Test
1127     void testSingletonsChunked() {
1128         assertEquals("AA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0 })));
1129         assertEquals("AQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 1 })));
1130         assertEquals("Ag==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 2 })));
1131         assertEquals("Aw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 3 })));
1132         assertEquals("BA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 4 })));
1133         assertEquals("BQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 5 })));
1134         assertEquals("Bg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 6 })));
1135         assertEquals("Bw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 7 })));
1136         assertEquals("CA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 8 })));
1137         assertEquals("CQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 9 })));
1138         assertEquals("Cg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 10 })));
1139         assertEquals("Cw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 11 })));
1140         assertEquals("DA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 12 })));
1141         assertEquals("DQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 13 })));
1142         assertEquals("Dg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 14 })));
1143         assertEquals("Dw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 15 })));
1144         assertEquals("EA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 16 })));
1145         assertEquals("EQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 17 })));
1146         assertEquals("Eg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 18 })));
1147         assertEquals("Ew==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 19 })));
1148         assertEquals("FA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 20 })));
1149         assertEquals("FQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 21 })));
1150         assertEquals("Fg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 22 })));
1151         assertEquals("Fw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 23 })));
1152         assertEquals("GA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 24 })));
1153         assertEquals("GQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 25 })));
1154         assertEquals("Gg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 26 })));
1155         assertEquals("Gw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 27 })));
1156         assertEquals("HA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 28 })));
1157         assertEquals("HQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 29 })));
1158         assertEquals("Hg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 30 })));
1159         assertEquals("Hw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 31 })));
1160         assertEquals("IA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 32 })));
1161         assertEquals("IQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 33 })));
1162         assertEquals("Ig==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 34 })));
1163         assertEquals("Iw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 35 })));
1164         assertEquals("JA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 36 })));
1165         assertEquals("JQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 37 })));
1166         assertEquals("Jg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 38 })));
1167         assertEquals("Jw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 39 })));
1168         assertEquals("KA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 40 })));
1169         assertEquals("KQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 41 })));
1170         assertEquals("Kg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 42 })));
1171         assertEquals("Kw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 43 })));
1172         assertEquals("LA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 44 })));
1173         assertEquals("LQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 45 })));
1174         assertEquals("Lg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 46 })));
1175         assertEquals("Lw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 47 })));
1176         assertEquals("MA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 48 })));
1177         assertEquals("MQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 49 })));
1178         assertEquals("Mg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 50 })));
1179         assertEquals("Mw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 51 })));
1180         assertEquals("NA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 52 })));
1181         assertEquals("NQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 53 })));
1182         assertEquals("Ng==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 54 })));
1183         assertEquals("Nw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 55 })));
1184         assertEquals("OA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 56 })));
1185         assertEquals("OQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 57 })));
1186         assertEquals("Og==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 58 })));
1187         assertEquals("Ow==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 59 })));
1188         assertEquals("PA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 60 })));
1189         assertEquals("PQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 61 })));
1190         assertEquals("Pg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 62 })));
1191         assertEquals("Pw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 63 })));
1192         assertEquals("QA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 64 })));
1193         assertEquals("QQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 65 })));
1194         assertEquals("Qg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 66 })));
1195         assertEquals("Qw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 67 })));
1196         assertEquals("RA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 68 })));
1197         assertEquals("RQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 69 })));
1198         assertEquals("Rg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 70 })));
1199         assertEquals("Rw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 71 })));
1200         assertEquals("SA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 72 })));
1201         assertEquals("SQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 73 })));
1202         assertEquals("Sg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 74 })));
1203         assertEquals("Sw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 75 })));
1204         assertEquals("TA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 76 })));
1205         assertEquals("TQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 77 })));
1206         assertEquals("Tg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 78 })));
1207         assertEquals("Tw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 79 })));
1208         assertEquals("UA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 80 })));
1209         assertEquals("UQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 81 })));
1210         assertEquals("Ug==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 82 })));
1211         assertEquals("Uw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 83 })));
1212         assertEquals("VA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 84 })));
1213         assertEquals("VQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 85 })));
1214         assertEquals("Vg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 86 })));
1215         assertEquals("Vw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 87 })));
1216         assertEquals("WA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 88 })));
1217         assertEquals("WQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 89 })));
1218         assertEquals("Wg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 90 })));
1219         assertEquals("Ww==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 91 })));
1220         assertEquals("XA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 92 })));
1221         assertEquals("XQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 93 })));
1222         assertEquals("Xg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 94 })));
1223         assertEquals("Xw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 95 })));
1224         assertEquals("YA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 96 })));
1225         assertEquals("YQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 97 })));
1226         assertEquals("Yg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 98 })));
1227         assertEquals("Yw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 99 })));
1228         assertEquals("ZA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 100 })));
1229         assertEquals("ZQ==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 101 })));
1230         assertEquals("Zg==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 102 })));
1231         assertEquals("Zw==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 103 })));
1232         assertEquals("aA==\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 104 })));
1233     }
1234 
1235     @Test
1236     void testStringToByteVariations() throws DecoderException {
1237         final Base64 base64 = new Base64();
1238         final String s1 = "SGVsbG8gV29ybGQ=\r\n";
1239         final String s2 = "";
1240         final String s3 = null;
1241         final String s4a = "K/fMJwH+Q5e0nr7tWsxwkA==\r\n";
1242         final String s4b = "K_fMJwH-Q5e0nr7tWsxwkA";
1243         final byte[] b4 = Hex.decodeHex("2bf7cc2701fe4397b49ebeed5acc7090"); // for
1244                                                                                             // url-safe
1245                                                                                             // tests
1246 
1247         assertEquals("Hello World", StringUtils.newStringUtf8(base64.decode(s1)), "StringToByte Hello World");
1248         assertEquals("Hello World", StringUtils.newStringUtf8((byte[]) base64.decode((Object) s1)), "StringToByte Hello World");
1249         assertEquals("Hello World", StringUtils.newStringUtf8(Base64.decodeBase64(s1)), "StringToByte static Hello World");
1250         assertEquals("", StringUtils.newStringUtf8(base64.decode(s2)), "StringToByte \"\"");
1251         assertEquals("", StringUtils.newStringUtf8(Base64.decodeBase64(s2)), "StringToByte static \"\"");
1252         assertNull(StringUtils.newStringUtf8(base64.decode(s3)), "StringToByte null");
1253         assertNull(StringUtils.newStringUtf8(Base64.decodeBase64(s3)), "StringToByte static null");
1254         assertArrayEquals(b4, base64.decode(s4b), "StringToByte UUID");
1255         assertArrayEquals(b4, Base64.decodeBase64(s4a), "StringToByte static UUID");
1256         assertArrayEquals(b4, Base64.decodeBase64(s4b), "StringToByte static-url-safe UUID");
1257     }
1258 
1259     @Test
1260     void testTriplets() {
1261         assertEquals("AAAA", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 0 })));
1262         assertEquals("AAAB", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 1 })));
1263         assertEquals("AAAC", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 2 })));
1264         assertEquals("AAAD", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 3 })));
1265         assertEquals("AAAE", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 4 })));
1266         assertEquals("AAAF", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 5 })));
1267         assertEquals("AAAG", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 6 })));
1268         assertEquals("AAAH", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 7 })));
1269         assertEquals("AAAI", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 8 })));
1270         assertEquals("AAAJ", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 9 })));
1271         assertEquals("AAAK", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 10 })));
1272         assertEquals("AAAL", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 11 })));
1273         assertEquals("AAAM", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 12 })));
1274         assertEquals("AAAN", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 13 })));
1275         assertEquals("AAAO", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 14 })));
1276         assertEquals("AAAP", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 15 })));
1277         assertEquals("AAAQ", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 16 })));
1278         assertEquals("AAAR", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 17 })));
1279         assertEquals("AAAS", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 18 })));
1280         assertEquals("AAAT", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 19 })));
1281         assertEquals("AAAU", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 20 })));
1282         assertEquals("AAAV", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 21 })));
1283         assertEquals("AAAW", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 22 })));
1284         assertEquals("AAAX", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 23 })));
1285         assertEquals("AAAY", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 24 })));
1286         assertEquals("AAAZ", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 25 })));
1287         assertEquals("AAAa", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 26 })));
1288         assertEquals("AAAb", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 27 })));
1289         assertEquals("AAAc", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 28 })));
1290         assertEquals("AAAd", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 29 })));
1291         assertEquals("AAAe", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 30 })));
1292         assertEquals("AAAf", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 31 })));
1293         assertEquals("AAAg", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 32 })));
1294         assertEquals("AAAh", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 33 })));
1295         assertEquals("AAAi", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 34 })));
1296         assertEquals("AAAj", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 35 })));
1297         assertEquals("AAAk", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 36 })));
1298         assertEquals("AAAl", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 37 })));
1299         assertEquals("AAAm", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 38 })));
1300         assertEquals("AAAn", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 39 })));
1301         assertEquals("AAAo", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 40 })));
1302         assertEquals("AAAp", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 41 })));
1303         assertEquals("AAAq", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 42 })));
1304         assertEquals("AAAr", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 43 })));
1305         assertEquals("AAAs", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 44 })));
1306         assertEquals("AAAt", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 45 })));
1307         assertEquals("AAAu", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 46 })));
1308         assertEquals("AAAv", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 47 })));
1309         assertEquals("AAAw", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 48 })));
1310         assertEquals("AAAx", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 49 })));
1311         assertEquals("AAAy", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 50 })));
1312         assertEquals("AAAz", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 51 })));
1313         assertEquals("AAA0", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 52 })));
1314         assertEquals("AAA1", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 53 })));
1315         assertEquals("AAA2", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 54 })));
1316         assertEquals("AAA3", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 55 })));
1317         assertEquals("AAA4", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 56 })));
1318         assertEquals("AAA5", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 57 })));
1319         assertEquals("AAA6", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 58 })));
1320         assertEquals("AAA7", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 59 })));
1321         assertEquals("AAA8", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 60 })));
1322         assertEquals("AAA9", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 61 })));
1323         assertEquals("AAA+", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 62 })));
1324         assertEquals("AAA/", new String(Base64.encodeBase64(new byte[] { (byte) 0, (byte) 0, (byte) 63 })));
1325     }
1326 
1327     @Test
1328     void testTripletsChunked() {
1329         assertEquals("AAAA\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 0 })));
1330         assertEquals("AAAB\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 1 })));
1331         assertEquals("AAAC\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 2 })));
1332         assertEquals("AAAD\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 3 })));
1333         assertEquals("AAAE\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 4 })));
1334         assertEquals("AAAF\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 5 })));
1335         assertEquals("AAAG\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 6 })));
1336         assertEquals("AAAH\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 7 })));
1337         assertEquals("AAAI\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 8 })));
1338         assertEquals("AAAJ\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 9 })));
1339         assertEquals("AAAK\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 10 })));
1340         assertEquals("AAAL\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 11 })));
1341         assertEquals("AAAM\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 12 })));
1342         assertEquals("AAAN\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 13 })));
1343         assertEquals("AAAO\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 14 })));
1344         assertEquals("AAAP\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 15 })));
1345         assertEquals("AAAQ\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 16 })));
1346         assertEquals("AAAR\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 17 })));
1347         assertEquals("AAAS\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 18 })));
1348         assertEquals("AAAT\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 19 })));
1349         assertEquals("AAAU\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 20 })));
1350         assertEquals("AAAV\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 21 })));
1351         assertEquals("AAAW\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 22 })));
1352         assertEquals("AAAX\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 23 })));
1353         assertEquals("AAAY\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 24 })));
1354         assertEquals("AAAZ\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 25 })));
1355         assertEquals("AAAa\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 26 })));
1356         assertEquals("AAAb\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 27 })));
1357         assertEquals("AAAc\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 28 })));
1358         assertEquals("AAAd\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 29 })));
1359         assertEquals("AAAe\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 30 })));
1360         assertEquals("AAAf\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 31 })));
1361         assertEquals("AAAg\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 32 })));
1362         assertEquals("AAAh\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 33 })));
1363         assertEquals("AAAi\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 34 })));
1364         assertEquals("AAAj\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 35 })));
1365         assertEquals("AAAk\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 36 })));
1366         assertEquals("AAAl\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 37 })));
1367         assertEquals("AAAm\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 38 })));
1368         assertEquals("AAAn\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 39 })));
1369         assertEquals("AAAo\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 40 })));
1370         assertEquals("AAAp\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 41 })));
1371         assertEquals("AAAq\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 42 })));
1372         assertEquals("AAAr\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 43 })));
1373         assertEquals("AAAs\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 44 })));
1374         assertEquals("AAAt\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 45 })));
1375         assertEquals("AAAu\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 46 })));
1376         assertEquals("AAAv\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 47 })));
1377         assertEquals("AAAw\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 48 })));
1378         assertEquals("AAAx\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 49 })));
1379         assertEquals("AAAy\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 50 })));
1380         assertEquals("AAAz\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 51 })));
1381         assertEquals("AAA0\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 52 })));
1382         assertEquals("AAA1\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 53 })));
1383         assertEquals("AAA2\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 54 })));
1384         assertEquals("AAA3\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 55 })));
1385         assertEquals("AAA4\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 56 })));
1386         assertEquals("AAA5\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 57 })));
1387         assertEquals("AAA6\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 58 })));
1388         assertEquals("AAA7\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 59 })));
1389         assertEquals("AAA8\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 60 })));
1390         assertEquals("AAA9\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 61 })));
1391         assertEquals("AAA+\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 62 })));
1392         assertEquals("AAA/\r\n", new String(Base64.encodeBase64Chunked(new byte[] { (byte) 0, (byte) 0, (byte) 63 })));
1393     }
1394 
1395     /**
1396      * Tests URL-safe Base64 against random data, sizes 0 to 150.
1397      */
1398     @Test
1399     void testUrlSafe() {
1400         // test random data of sizes 0 through 150
1401         final BaseNCodec codec = new Base64(true);
1402         for (int i = 0; i <= 150; i++) {
1403             final byte[][] randomData = BaseNTestData.randomData(codec, i);
1404             final byte[] encoded = randomData[1];
1405             final byte[] decoded = randomData[0];
1406             final byte[] result = Base64.decodeBase64(encoded);
1407             assertArrayEquals(decoded, result, "url-safe i=" + i);
1408             assertFalse(BaseNTestData.bytesContain(encoded, (byte) '='), "url-safe i=" + i + " no '='");
1409             assertFalse(BaseNTestData.bytesContain(encoded, (byte) '\\'), "url-safe i=" + i + " no '\\'");
1410             assertFalse(BaseNTestData.bytesContain(encoded, (byte) '+'), "url-safe i=" + i + " no '+'");
1411         }
1412 
1413     }
1414 
1415     /**
1416      * Base64 encoding of UUID's is a common use-case, especially in URL-SAFE
1417      * mode. This test case ends up being the "URL-SAFE" JUnit's.
1418      *
1419      * @throws DecoderException
1420      *             if Hex.decode() fails - a serious problem since Hex comes
1421      *             from our own commons-codec!
1422      */
1423     @Test
1424     void testUUID() throws DecoderException {
1425         // The 4 UUID's below contains mixtures of + and / to help us test the
1426         // URL-SAFE encoding mode.
1427         final byte[][] ids = new byte[4][];
1428 
1429         // ids[0] was chosen so that it encodes with at least one +.
1430         ids[0] = Hex.decodeHex("94ed8d0319e4493399560fb67404d370");
1431 
1432         // ids[1] was chosen so that it encodes with both / and +.
1433         ids[1] = Hex.decodeHex("2bf7cc2701fe4397b49ebeed5acc7090");
1434 
1435         // ids[2] was chosen so that it encodes with at least one /.
1436         ids[2] = Hex.decodeHex("64be154b6ffa40258d1a01288e7c31ca");
1437 
1438         // ids[3] was chosen so that it encodes with both / and +, with /
1439         // right at the beginning.
1440         ids[3] = Hex.decodeHex("ff7f8fc01cdb471a8c8b5a9306183fe8");
1441 
1442         final byte[][] standard = new byte[4][];
1443         standard[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg+2dATTcA==");
1444         standard[1] = StringUtils.getBytesUtf8("K/fMJwH+Q5e0nr7tWsxwkA==");
1445         standard[2] = StringUtils.getBytesUtf8("ZL4VS2/6QCWNGgEojnwxyg==");
1446         standard[3] = StringUtils.getBytesUtf8("/3+PwBzbRxqMi1qTBhg/6A==");
1447 
1448         final byte[][] urlSafe1 = new byte[4][];
1449         // regular padding (two '==' signs).
1450         urlSafe1[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg-2dATTcA==");
1451         urlSafe1[1] = StringUtils.getBytesUtf8("K_fMJwH-Q5e0nr7tWsxwkA==");
1452         urlSafe1[2] = StringUtils.getBytesUtf8("ZL4VS2_6QCWNGgEojnwxyg==");
1453         urlSafe1[3] = StringUtils.getBytesUtf8("_3-PwBzbRxqMi1qTBhg_6A==");
1454 
1455         final byte[][] urlSafe2 = new byte[4][];
1456         // single padding (only one '=' sign).
1457         urlSafe2[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg-2dATTcA=");
1458         urlSafe2[1] = StringUtils.getBytesUtf8("K_fMJwH-Q5e0nr7tWsxwkA=");
1459         urlSafe2[2] = StringUtils.getBytesUtf8("ZL4VS2_6QCWNGgEojnwxyg=");
1460         urlSafe2[3] = StringUtils.getBytesUtf8("_3-PwBzbRxqMi1qTBhg_6A=");
1461 
1462         final byte[][] urlSafe3 = new byte[4][];
1463         // no padding (no '=' signs).
1464         urlSafe3[0] = StringUtils.getBytesUtf8("lO2NAxnkSTOZVg-2dATTcA");
1465         urlSafe3[1] = StringUtils.getBytesUtf8("K_fMJwH-Q5e0nr7tWsxwkA");
1466         urlSafe3[2] = StringUtils.getBytesUtf8("ZL4VS2_6QCWNGgEojnwxyg");
1467         urlSafe3[3] = StringUtils.getBytesUtf8("_3-PwBzbRxqMi1qTBhg_6A");
1468 
1469         for (int i = 0; i < 4; i++) {
1470             final byte[] encodedStandard = Base64.encodeBase64(ids[i]);
1471             final byte[] encodedUrlSafe = Base64.encodeBase64URLSafe(ids[i]);
1472             final byte[] decodedStandard = Base64.decodeBase64(standard[i]);
1473             final byte[] decodedUrlSafe1 = Base64.decodeBase64(urlSafe1[i]);
1474             final byte[] decodedUrlSafe2 = Base64.decodeBase64(urlSafe2[i]);
1475             final byte[] decodedUrlSafe3 = Base64.decodeBase64(urlSafe3[i]);
1476 
1477             // Very important debugging output should anyone
1478             // ever need to delve closely into this stuff.
1479 //            {
1480 //                System.out.println("reference: [" + Hex.encodeHexString(ids[i]) + "]");
1481 //                System.out.println("standard:  [" + Hex.encodeHexString(decodedStandard) + "] From: ["
1482 //                        + StringUtils.newStringUtf8(standard[i]) + "]");
1483 //                System.out.println("safe1:     [" + Hex.encodeHexString(decodedUrlSafe1) + "] From: ["
1484 //                        + StringUtils.newStringUtf8(urlSafe1[i]) + "]");
1485 //                System.out.println("safe2:     [" + Hex.encodeHexString(decodedUrlSafe2) + "] From: ["
1486 //                        + StringUtils.newStringUtf8(urlSafe2[i]) + "]");
1487 //                System.out.println("safe3:     [" + Hex.encodeHexString(decodedUrlSafe3) + "] From: ["
1488 //                        + StringUtils.newStringUtf8(urlSafe3[i]) + "]");
1489 //            }
1490 
1491             assertArrayEquals(encodedStandard, standard[i], "standard encode uuid");
1492             assertArrayEquals(encodedUrlSafe, urlSafe3[i], "url-safe encode uuid");
1493             assertArrayEquals(decodedStandard, ids[i], "standard decode uuid");
1494             assertArrayEquals(decodedUrlSafe1, ids[i], "url-safe1 decode uuid");
1495             assertArrayEquals(decodedUrlSafe2, ids[i], "url-safe2 decode uuid");
1496             assertArrayEquals(decodedUrlSafe3, ids[i], "url-safe3 decode uuid");
1497         }
1498     }
1499 
1500     private String toString(final byte[] data) {
1501         final StringBuilder buf = new StringBuilder();
1502         for (int i = 0; i < data.length; i++) {
1503             buf.append(data[i]);
1504             if (i != data.length - 1) {
1505                 buf.append(",");
1506             }
1507         }
1508         return buf.toString();
1509     }
1510 }