Skip to content

Commit 11a4ed6

Browse files
committed
Minor feature add for base64 encoder to support linefeeds other than quoted-for-json
1 parent 152e80c commit 11a4ed6

File tree

2 files changed

+86
-14
lines changed

2 files changed

+86
-14
lines changed

src/main/java/com/fasterxml/jackson/core/Base64Variant.java

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public final class Base64Variant
8888
private final transient boolean _usesPadding;
8989

9090
/**
91-
* Characted used for padding, if any ({@link #PADDING_CHAR_NONE} if not).
91+
* Character used for padding, if any ({@link #PADDING_CHAR_NONE} if not).
9292
*/
9393
private final transient char _paddingChar;
9494

@@ -367,21 +367,17 @@ public String encode(byte[] input)
367367

368368
/**
369369
* Convenience method for converting given byte array as base64 encoded String
370-
* using this variant's settings,
371-
* optionally enclosed in double-quotes.
370+
* using this variant's settings, optionally enclosed in double-quotes.
371+
* Linefeeds added, if needed, are expressed as 2-character JSON (and Java source)
372+
* escape sequence of backslash + `n`.
372373
*
373374
* @param input Byte array to encode
374375
* @param addQuotes Whether to surround resulting value in double quotes or not
375376
*/
376377
public String encode(byte[] input, boolean addQuotes)
377378
{
378-
int inputEnd = input.length;
379-
StringBuilder sb;
380-
{
381-
// let's approximate... 33% overhead, ~= 3/8 (0.375)
382-
int outputLen = inputEnd + (inputEnd >> 2) + (inputEnd >> 3);
383-
sb = new StringBuilder(outputLen);
384-
}
379+
final int inputEnd = input.length;
380+
final StringBuilder sb = new StringBuilder(inputEnd + (inputEnd >> 2) + (inputEnd >> 3));
385381
if (addQuotes) {
386382
sb.append('"');
387383
}
@@ -422,13 +418,61 @@ public String encode(byte[] input, boolean addQuotes)
422418
return sb.toString();
423419
}
424420

421+
/**
422+
* Convenience method for converting given byte array as base64 encoded String
423+
* using this variant's settings, optionally enclosed in double-quotes.
424+
* Linefeed character to use is passed explicitly.
425+
*
426+
* @param input Byte array to encode
427+
* @param addQuotes Whether to surround resulting value in double quotes or not
428+
*
429+
* @since 2.10
430+
*/
431+
public String encode(byte[] input, boolean addQuotes, String linefeed)
432+
{
433+
final int inputEnd = input.length;
434+
final StringBuilder sb = new StringBuilder(inputEnd + (inputEnd >> 2) + (inputEnd >> 3));
435+
if (addQuotes) {
436+
sb.append('"');
437+
}
438+
439+
int chunksBeforeLF = getMaxLineLength() >> 2;
440+
441+
int inputPtr = 0;
442+
int safeInputEnd = inputEnd-3;
443+
444+
while (inputPtr <= safeInputEnd) {
445+
int b24 = ((int) input[inputPtr++]) << 8;
446+
b24 |= ((int) input[inputPtr++]) & 0xFF;
447+
b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
448+
encodeBase64Chunk(sb, b24);
449+
if (--chunksBeforeLF <= 0) {
450+
sb.append(linefeed);
451+
chunksBeforeLF = getMaxLineLength() >> 2;
452+
}
453+
}
454+
int inputLeft = inputEnd - inputPtr;
455+
if (inputLeft > 0) {
456+
int b24 = ((int) input[inputPtr++]) << 16;
457+
if (inputLeft == 2) {
458+
b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
459+
}
460+
encodeBase64Partial(sb, b24, inputLeft);
461+
}
462+
463+
if (addQuotes) {
464+
sb.append('"');
465+
}
466+
return sb.toString();
467+
}
468+
425469
/**
426470
* Convenience method for decoding contents of a Base64-encoded String,
427471
* using this variant's settings.
428-
*
472+
*
429473
* @param input
430-
*
431-
* @since 2.2.3
474+
*
475+
* @since 2.3
432476
*
433477
* @throws IllegalArgumentException if input is not valid base64 encoded data
434478
*/

src/test/java/com/fasterxml/jackson/core/base64/Base64CodecTest.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.fasterxml.jackson.core.base64;
22

3+
import java.util.Arrays;
4+
35
import org.junit.Assert;
46

57
import com.fasterxml.jackson.core.*;
@@ -76,7 +78,7 @@ public void testCharEncoding() throws Exception
7678

7779
public void testConvenienceMethods() throws Exception
7880
{
79-
Base64Variant std = Base64Variants.MIME;
81+
final Base64Variant std = Base64Variants.MIME;
8082

8183
byte[] input = new byte[] { 1, 2, 34, 127, -1 };
8284
String encoded = std.encode(input, false);
@@ -96,6 +98,32 @@ public void testConvenienceMethods() throws Exception
9698
Assert.assertArrayEquals(input, decoded);
9799
}
98100

101+
public void testConvenienceMethodWithLFs() throws Exception
102+
{
103+
final Base64Variant std = Base64Variants.MIME;
104+
105+
final int length = 100;
106+
final byte[] data = new byte[length];
107+
Arrays.fill(data, (byte) 1);
108+
109+
final StringBuilder sb = new StringBuilder(140);
110+
for (int i = 0; i < 100/3; ++i) {
111+
sb.append("AQEB");
112+
if (sb.length() == 76) {
113+
sb.append("##");
114+
}
115+
}
116+
sb.append("AQ==");
117+
final String exp = sb.toString();
118+
119+
// first, JSON standard
120+
assertEquals(exp.replace("##", "\\n"), std.encode(data, false));
121+
122+
// then with custom linefeed
123+
124+
assertEquals(exp.replace("##", "<%>"), std.encode(data, false, "<%>"));
125+
}
126+
99127
@SuppressWarnings("unused")
100128
public void testErrors() throws Exception
101129
{

0 commit comments

Comments
 (0)