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