This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-codec.git
commit 937df546016b229fcea57ca03076c887d3849f7e Author: Gary D. Gregory <[email protected]> AuthorDate: Sun Oct 12 15:46:55 2025 -0400 Internal refactoring Add missing test --- .../org/apache/commons/codec/binary/Base32.java | 20 +++--- .../org/apache/commons/codec/binary/Base64.java | 73 ++++++++-------------- .../apache/commons/codec/binary/BaseNCodec.java | 5 ++ .../apache/commons/codec/binary/Base32Test.java | 3 +- .../apache/commons/codec/binary/Base64Test.java | 61 ++++++++++-------- 5 files changed, 76 insertions(+), 86 deletions(-) diff --git a/src/main/java/org/apache/commons/codec/binary/Base32.java b/src/main/java/org/apache/commons/codec/binary/Base32.java index 509a255d..0beac482 100644 --- a/src/main/java/org/apache/commons/codec/binary/Base32.java +++ b/src/main/java/org/apache/commons/codec/binary/Base32.java @@ -345,20 +345,16 @@ public class Base32 extends BaseNCodec { super(builder); Objects.requireNonNull(builder.getEncodeTable(), "encodeTable"); this.encodeTable = builder.getEncodeTable(); - this.decodeTable = builder.getEncodeTable() == HEX_ENCODE_TABLE || Arrays.equals(builder.getEncodeTable(), HEX_ENCODE_TABLE) ? - HEX_DECODE_TABLE : DECODE_TABLE; + this.decodeTable = Arrays.equals(builder.getEncodeTable(), HEX_ENCODE_TABLE) ? HEX_DECODE_TABLE : DECODE_TABLE; if (builder.getLineLength() > 0) { - if (builder.getLineSeparator() == null) { - throw new IllegalArgumentException("lineLength " + lineLength + " > 0, but lineSeparator is null"); - } - final byte[] lineSeparatorCopy = builder.getLineSeparator().clone(); + final byte[] lineSeparator = builder.getLineSeparator(); // Must be done after initializing the tables - if (containsAlphabetOrPad(lineSeparatorCopy)) { - final String sep = StringUtils.newStringUtf8(lineSeparatorCopy); + if (containsAlphabetOrPad(lineSeparator)) { + final String sep = StringUtils.newStringUtf8(lineSeparator); throw new IllegalArgumentException("lineSeparator must not contain Base32 characters: [" + sep + "]"); } - this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparatorCopy.length; - this.lineSeparator = lineSeparatorCopy; + this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; + this.lineSeparator = lineSeparator; } else { this.encodeSize = BYTES_PER_ENCODED_BLOCK; this.lineSeparator = null; @@ -499,8 +495,8 @@ public class Base32 extends BaseNCodec { */ @Deprecated public Base32(final int lineLength, final byte[] lineSeparator, final boolean useHex, final byte padding, final CodecPolicy decodingPolicy) { - this(builder().setLineLength(lineLength).setLineSeparator(lineSeparator).setEncodeTableRaw(encodeTable(useHex)).setPadding(padding) - .setDecodingPolicy(decodingPolicy)); + this(builder().setLineLength(lineLength).setLineSeparator(lineSeparator != null ? lineSeparator : EMPTY_BYTE_ARRAY) + .setEncodeTableRaw(encodeTable(useHex)).setPadding(padding).setDecodingPolicy(decodingPolicy)); } /** diff --git a/src/main/java/org/apache/commons/codec/binary/Base64.java b/src/main/java/org/apache/commons/codec/binary/Base64.java index df0c1a60..9950d04d 100644 --- a/src/main/java/org/apache/commons/codec/binary/Base64.java +++ b/src/main/java/org/apache/commons/codec/binary/Base64.java @@ -98,11 +98,13 @@ public class Base64 extends BaseNCodec { */ public Builder() { super(STANDARD_ENCODE_TABLE); + setEncodedBlockSize(BYTES_PER_ENCODED_BLOCK); + setUnencodedBlockSize(BYTES_PER_UNENCODED_BLOCK); } @Override public Base64 get() { - return new Base64(getLineLength(), getLineSeparator(), getPadding(), getEncodeTable(), getDecodingPolicy()); + return new Base64(this); } /** @@ -529,6 +531,9 @@ public class Base64 extends BaseNCodec { private final boolean isUrlSafe; + private final boolean isStandardEncodeTable; + + /** * Constructs a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. * <p> @@ -646,7 +651,8 @@ public class Base64 extends BaseNCodec { */ @Deprecated public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) { - this(lineLength, lineSeparator, PAD_DEFAULT, toUrlSafeEncodeTable(urlSafe), DECODING_POLICY_DEFAULT); + this(builder().setLineLength(lineLength).setLineSeparator(lineSeparator != null ? lineSeparator : EMPTY_BYTE_ARRAY).setPadding(PAD_DEFAULT) + .setEncodeTableRaw(toUrlSafeEncodeTable(urlSafe)).setDecodingPolicy(DECODING_POLICY_DEFAULT)); } /** @@ -680,56 +686,31 @@ public class Base64 extends BaseNCodec { */ @Deprecated public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe, final CodecPolicy decodingPolicy) { - this(lineLength, lineSeparator, PAD_DEFAULT, toUrlSafeEncodeTable(urlSafe), decodingPolicy); + this(builder().setLineLength(lineLength).setLineSeparator(lineSeparator).setPadding(PAD_DEFAULT).setEncodeTableRaw(toUrlSafeEncodeTable(urlSafe)) + .setDecodingPolicy(decodingPolicy)); } - /** - * Constructs a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode. - * <p> - * When encoding the line length and line separator are given in the constructor, and the encoding table is STANDARD_ENCODE_TABLE. - * </p> - * <p> - * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. - * </p> - * <p> - * When decoding all variants are supported. - * </p> - * - * @param lineLength Each line of encoded data will be at most of the given length (rounded down to the nearest multiple of 4). If lineLength <= 0, - * then the output will not be divided into lines (chunks). Ignored when decoding. - * @param lineSeparator Each line of encoded data will end with this sequence of bytes; the constructor makes a defensive copy. May be null. - * @param padding padding byte. - * @param encodeTable The manual encodeTable - a byte array of 64 chars. - * @param decodingPolicy The decoding policy. - * @throws IllegalArgumentException Thrown when the {@code lineSeparator} contains Base64 characters. - */ - private Base64(final int lineLength, final byte[] lineSeparator, final byte padding, final byte[] encodeTable, final CodecPolicy decodingPolicy) { - super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, lineLength, toLength(lineSeparator), padding, decodingPolicy); - Objects.requireNonNull(encodeTable, "encodeTable"); - if (encodeTable.length != STANDARD_ENCODE_TABLE.length) { + private Base64(final Builder builder) { + super(builder); + Objects.requireNonNull(builder.getEncodeTable(), "encodeTable"); + if (builder.getEncodeTable().length != STANDARD_ENCODE_TABLE.length) { throw new IllegalArgumentException("encodeTable must have exactly 64 entries."); } - // same array first or equal contents second - this.isUrlSafe = encodeTable == URL_SAFE_ENCODE_TABLE || Arrays.equals(encodeTable, URL_SAFE_ENCODE_TABLE); - if (encodeTable == STANDARD_ENCODE_TABLE || this.isUrlSafe) { - decodeTable = DECODE_TABLE; - // No need of a defensive copy of an internal table. - this.encodeTable = encodeTable; - } else { - this.encodeTable = encodeTable.clone(); - this.decodeTable = calculateDecodeTable(this.encodeTable); - } + this.isStandardEncodeTable = Arrays.equals(builder.getEncodeTable(), STANDARD_ENCODE_TABLE); + this.isUrlSafe = Arrays.equals(builder.getEncodeTable(), URL_SAFE_ENCODE_TABLE); + this.encodeTable = builder.getEncodeTable(); + this.decodeTable = this.isStandardEncodeTable || this.isUrlSafe ? DECODE_TABLE : calculateDecodeTable(this.encodeTable); // TODO could be simplified if there is no requirement to reject invalid line sep when length <=0 // @see test case Base64Test.testConstructors() - if (lineSeparator != null) { - final byte[] lineSeparatorCopy = lineSeparator.clone(); - if (containsAlphabetOrPad(lineSeparatorCopy)) { - final String sep = StringUtils.newStringUtf8(lineSeparatorCopy); + if (builder.getLineSeparator().length > 0) { + final byte[] lineSeparatorB = builder.getLineSeparator(); + if (containsAlphabetOrPad(lineSeparatorB)) { + final String sep = StringUtils.newStringUtf8(lineSeparatorB); throw new IllegalArgumentException("lineSeparator must not contain base64 characters: [" + sep + "]"); } - if (lineLength > 0) { // null line-sep forces no chunking rather than throwing IAE - this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparatorCopy.length; - this.lineSeparator = lineSeparatorCopy; + if (builder.getLineLength() > 0) { // null line-sep forces no chunking rather than throwing IAE + this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparatorB.length; + this.lineSeparator = lineSeparatorB; } else { this.encodeSize = BYTES_PER_ENCODED_BLOCK; this.lineSeparator = null; @@ -885,7 +866,7 @@ public class Base64 extends BaseNCodec { // remaining 2: buffer[context.pos++] = encodeTable[context.ibitWorkArea << 4 & MASK_6_BITS]; // URL-SAFE skips the padding to further reduce size. - if (encodeTable == STANDARD_ENCODE_TABLE) { + if (isStandardEncodeTable) { buffer[context.pos++] = pad; buffer[context.pos++] = pad; } @@ -896,7 +877,7 @@ public class Base64 extends BaseNCodec { buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 4 & MASK_6_BITS]; buffer[context.pos++] = encodeTable[context.ibitWorkArea << 2 & MASK_6_BITS]; // URL-SAFE skips the padding to further reduce size. - if (encodeTable == STANDARD_ENCODE_TABLE) { + if (isStandardEncodeTable) { buffer[context.pos++] = pad; } break; diff --git a/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java b/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java index d745f865..59c785c3 100644 --- a/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java +++ b/src/main/java/org/apache/commons/codec/binary/BaseNCodec.java @@ -348,6 +348,11 @@ public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder { */ static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; + /** + * The empty byte array. + */ + static final byte[] EMPTY_BYTE_ARRAY = {}; + /** * Create a positive capacity at least as large the minimum required capacity. * If the minimum capacity is negative then this throws an OutOfMemoryError as no array diff --git a/src/test/java/org/apache/commons/codec/binary/Base32Test.java b/src/test/java/org/apache/commons/codec/binary/Base32Test.java index 100d9176..43d61cea 100644 --- a/src/test/java/org/apache/commons/codec/binary/Base32Test.java +++ b/src/test/java/org/apache/commons/codec/binary/Base32Test.java @@ -427,13 +427,12 @@ class Base32Test { // even when line length is negative. base32 = new Base32(-1, new byte[] { 'A' }); base32 = new Base32(32, new byte[] { '$' }); // OK - assertArrayEquals(BaseNCodec.CHUNK_SEPARATOR, new Base32(32, null).getLineSeparator(), "null line separator use the default"); + assertArrayEquals(new byte[0], new Base32(32, null).getLineSeparator(), "null line separator use an empty array"); assertThrows(IllegalArgumentException.class, () -> new Base32(32, new byte[] { 'A' }), "'A' as a line separator"); assertThrows(IllegalArgumentException.class, () -> new Base32(32, new byte[] { '=' }), "'=' as a line separator"); assertThrows(IllegalArgumentException.class, () -> new Base32(32, new byte[] { 'A', '$' }), "'A$' as a line separator"); assertThrows(IllegalArgumentException.class, () -> new Base32(32, new byte[] { '\n' }, false, (byte) 'A'), "'A' as padding"); assertThrows(IllegalArgumentException.class, () -> new Base32(32, new byte[] { '\n' }, false, (byte) ' '), "' ' as padding"); - base32 = new Base32(32, new byte[] { ' ', '$', '\n', '\r', '\t' }); // OK assertNotNull(base32); } diff --git a/src/test/java/org/apache/commons/codec/binary/Base64Test.java b/src/test/java/org/apache/commons/codec/binary/Base64Test.java index 73ffed61..305ede94 100644 --- a/src/test/java/org/apache/commons/codec/binary/Base64Test.java +++ b/src/test/java/org/apache/commons/codec/binary/Base64Test.java @@ -31,6 +31,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Random; +import java.util.stream.Stream; import org.apache.commons.codec.CodecPolicy; import org.apache.commons.codec.DecoderException; @@ -38,6 +39,9 @@ import org.apache.commons.codec.EncoderException; import org.apache.commons.lang3.ArrayUtils; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** * Tests {@link Base64}. @@ -130,6 +134,24 @@ class Base64Test { } } + static Stream<Object> testIsBase64() { + // @formatter:off + return Stream.of( + Arguments.of(new byte[] { 1, 2, 3 }, false), + Arguments.of(new byte[] { Byte.MIN_VALUE }, false), + Arguments.of(new byte[] { -125 }, false), + Arguments.of(new byte[] { -10 }, false), + Arguments.of(new byte[] { 0 }, false), + Arguments.of(new byte[] { 64, Byte.MAX_VALUE }, false), + Arguments.of(new byte[] { Byte.MAX_VALUE }, false), + Arguments.of(new byte[] { 'A' }, true), + Arguments.of(new byte[] { 'A', Byte.MIN_VALUE }, false), + Arguments.of(new byte[] { 'A', 'Z', 'a' }, true), + Arguments.of(new byte[] { '/', '=', '+' }, true), + Arguments.of(new byte[] { '$' }, false)); + // @formatter:off + } + private final Random random = new Random(); /** @@ -149,22 +171,16 @@ class Base64Test { byte[] encodedBytes = Base64.encodeBase64(StringUtils.getBytesUtf8(content)); encodedContent = StringUtils.newStringUtf8(encodedBytes); assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world"); - - Base64 b64 = new Base64(BaseNCodec.MIME_CHUNK_SIZE, null); // null - // lineSeparator - // same as - // saying - // no-chunking + // null lineSeparator same as saying no-chunking + Base64 b64 = new Base64(BaseNCodec.MIME_CHUNK_SIZE, null); encodedBytes = b64.encode(StringUtils.getBytesUtf8(content)); encodedContent = StringUtils.newStringUtf8(encodedBytes); assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world"); - - b64 = new Base64(0, null); // null lineSeparator same as saying - // no-chunking + // null lineSeparator same as saying no-chunking + b64 = new Base64(0, null); encodedBytes = b64.encode(StringUtils.getBytesUtf8(content)); encodedContent = StringUtils.newStringUtf8(encodedBytes); assertEquals("SGVsbG8gV29ybGQ=", encodedContent, "encoding hello world"); - // bogus characters to decode (to skip actually) {e-acute*6} final byte[] decode = b64.decode("SGVsbG{\u00e9\u00e9\u00e9\u00e9\u00e9\u00e9}8gV29ybGQ="); final String decodeString = StringUtils.newStringUtf8(decode); @@ -692,23 +708,16 @@ class Base64Test { assertEquals(FOX_TEXT, new String(Base64.decodeBase64(FOX_BASE64.getBytes(CHARSET_UTF8)))); } - @Test - void testIsArrayByteBase64() { - assertFalse(Base64.isBase64(new byte[] { Byte.MIN_VALUE })); - assertFalse(Base64.isBase64(new byte[] { -125 })); - assertFalse(Base64.isBase64(new byte[] { -10 })); - assertFalse(Base64.isBase64(new byte[] { 0 })); - assertFalse(Base64.isBase64(new byte[] { 64, Byte.MAX_VALUE })); - assertFalse(Base64.isBase64(new byte[] { Byte.MAX_VALUE })); - - assertTrue(Base64.isBase64(new byte[] { 'A' })); - - assertFalse(Base64.isBase64(new byte[] { 'A', Byte.MIN_VALUE })); - - assertTrue(Base64.isBase64(new byte[] { 'A', 'Z', 'a' })); - assertTrue(Base64.isBase64(new byte[] { '/', '=', '+' })); + @ParameterizedTest + @MethodSource("testIsBase64") + void testIsArrayByteBase64(final byte[] arrayOctet, final boolean match) { + assertEquals(match, Base64.isArrayByteBase64(arrayOctet)); + } - assertFalse(Base64.isBase64(new byte[] { '$' })); + @ParameterizedTest + @MethodSource + void testIsBase64(final byte[] arrayOctet, final boolean match) { + assertEquals(match, Base64.isBase64(arrayOctet)); } /**
