Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 45 additions & 7 deletions src/main/java/org/apache/commons/lang3/ArrayFill.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@
*/
public final class ArrayFill {

/**
* Fills and returns the given array, assigning {@code 0} to each element of the array.
* <pre>
* ArrayFill.fill(a, (byte) 0);
* </pre>
*
* @param a the array to fill (may be null).
* @return the given array.
* @see Arrays#fill(byte[],byte)
* @see ArrayFill#fill(byte[],byte)
* @since 3.21.0
*/
public static byte[] clear(final byte[] a) {
return fill(a, (byte) 0);
}

/**
* Fills and returns the given array, assigning {@code '\0'} to each element of the array.
* <p>
Expand All @@ -50,19 +66,23 @@ public static char[] clear(final char[] a) {
}

/**
* Fills and returns the given array, assigning {@code 0} to each element of the array.
* Fills and returns the given array, assigning {@code '\0'} to each element of the array.
* <p>
* Equivalent to:
* </p>
* <pre>
* ArrayFill.fill(a, (byte) 0);
* ArrayFill.fill(a, fromIndex, toIndex, '\0'); // and not '0'!
* </pre>
*
* @param a the array to fill (may be null).
* @param a the array to fill (may be null).
* @param fromIndex the index of the first element (inclusive) to be filled with {@code '\0'}.
* @param toIndex the index of the last element (exclusive) to be filled with {@code '\0'}.
* @return the given array.
* @see Arrays#fill(byte[],byte)
* @see ArrayFill#fill(byte[],byte)
* @see Arrays#fill(char[], int, int, char)
* @since 3.21.0
*/
public static byte[] clear(final byte[] a) {
return fill(a, (byte) 0);
public static char[] clear(char[] a, int fromIndex, int toIndex) {
return fill(a, fromIndex, toIndex, CharUtils.NUL);
}

/**
Expand Down Expand Up @@ -111,6 +131,24 @@ public static char[] fill(final char[] a, final char val) {
return a;
}

/**
* Fills and returns the given array, assigning the given {@code char} value to each element of the array.
*
* @param a the array to fill (may be null).
* @param val the value to store in all elements of the array.
* @param fromIndex the index of the first element (inclusive) to be filled with the specified value.
* @param toIndex the index of the last element (exclusive) to be filled with the specified value.
* @return the given array.
* @see Arrays#fill(char[], int, int, char)
* @since 3.21.0
*/
public static char[] fill(char[] a, int fromIndex, int toIndex, char val) {
if (a != null) {
Arrays.fill(a, fromIndex, toIndex, val);
}
return a;
}

/**
* Fills and returns the given array, assigning the given {@code double} value to each element of the array.
*
Expand Down
35 changes: 22 additions & 13 deletions src/main/java/org/apache/commons/lang3/text/StrBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
import java.util.List;
import java.util.Objects;

import org.apache.commons.lang3.ArrayFill;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
Expand Down Expand Up @@ -1614,7 +1614,7 @@ public char charAt(final int index) {
*/
public StrBuilder clear() {
size = 0;
Arrays.fill(buffer, CharUtils.NUL);
ArrayFill.clear(buffer);
return this;
}

Expand Down Expand Up @@ -1810,7 +1810,7 @@ public StrBuilder deleteFirst(final StrMatcher matcher) {
private void deleteImpl(final int startIndex, final int endIndex, final int len) {
System.arraycopy(buffer, endIndex, buffer, startIndex, size - endIndex);
size -= len;
Arrays.fill(buffer, size, size + len, CharUtils.NUL);
ArrayFill.clear(buffer, size, size + len);
}

/**
Expand Down Expand Up @@ -1920,6 +1920,15 @@ public boolean equalsIgnoreCase(final StrBuilder other) {
return true;
}

/**
* Gets the internal buffer for testing.
*
* @return the internal buffer.
*/
char[] getBuffer() {
return buffer;
}

/**
* Copies the character array into the specified array.
*
Expand Down Expand Up @@ -2682,14 +2691,14 @@ public StrBuilder replaceFirst(final StrMatcher matcher, final String replaceStr
}

/**
* Internal method to delete a range without validation.
* Internal method to replace a range without validation.
*
* @param startIndex the start index, must be valid
* @param endIndex the end index (exclusive), must be valid
* @param removeLen the length to remove (endIndex - startIndex), must be valid
* @param insertStr the string to replace with, null means delete range
* @param insertLen the length of the insert string, must be valid
* @throws IndexOutOfBoundsException if any index is invalid
* @param startIndex the start index (inclusive), must be valid.
* @param endIndex the end index (exclusive), must be valid.
* @param removeLen the length to remove (endIndex - startIndex), must be valid.
* @param insertStr the string to replace with, null means delete range.
* @param insertLen the length of the insert string, must be valid.
* @throws IndexOutOfBoundsException if any index is invalid.
*/
private void replaceImpl(final int startIndex, final int endIndex, final int removeLen, final String insertStr, final int insertLen) {
final int newSize = size - removeLen + insertLen;
Expand Down Expand Up @@ -2815,12 +2824,12 @@ public StrBuilder setLength(final int length) {
throw new StringIndexOutOfBoundsException(length);
}
if (length < size) {
size = length;
ArrayFill.clear(buffer, length, size);
} else if (length > size) {
ensureCapacity(length);
Arrays.fill(buffer, size, length, CharUtils.NUL);
size = length;
ArrayFill.clear(buffer, size, length);
}
size = length;
return this;
}

Expand Down
45 changes: 45 additions & 0 deletions src/test/java/org/apache/commons/lang3/ArrayFillTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,24 @@ void testClearCharArrayNull() {
assertSame(array, actual);
}

@Test
void testClearCharArrayRange() {
final char[] array = {'A', 'B', 'C', 'D', 'E'};
final char[] actual = ArrayFill.clear(array, 1, 4);
assertSame(array, actual);
assertEquals('A', actual[0]);
assertEquals('\0', actual[1]);
assertEquals('\0', actual[2]);
assertEquals('\0', actual[3]);
assertEquals('E', actual[4]);
}

@Test
void testClearCharArrayRangeNull() {
final char[] actual = ArrayFill.clear(null, 0, 0);
assertNull(actual);
}

@Test
void testFillBooleanArray() {
final boolean[] array = new boolean[3];
Expand Down Expand Up @@ -123,6 +141,33 @@ void testFillCharArrayNull() {
assertSame(array, actual);
}

@Test
void testFillCharArrayRange() {
final char[] array = {'A', 'B', 'C', 'D', 'E'};
final char val = 'Z';
final char[] actual = ArrayFill.fill(array, 1, 4, val);
assertSame(array, actual);
assertEquals('A', actual[0]);
assertEquals('Z', actual[1]);
assertEquals('Z', actual[2]);
assertEquals('Z', actual[3]);
assertEquals('E', actual[4]);
}

@Test
void testFillCharArrayRangeEmpty() {
final char[] array = {'A', 'B', 'C'};
final char[] actual = ArrayFill.fill(array, 1, 1, 'Z');
assertSame(array, actual);
assertArrayEquals(new char[] {'A', 'B', 'C'}, actual);
}

@Test
void testFillCharArrayRangeNull() {
final char[] actual = ArrayFill.fill(null, 0, 0, 'Z');
assertNull(actual);
}

@Test
void testFillDoubleArray() {
final double[] array = new double[3];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@

package org.apache.commons.lang3.text;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Reader;
import java.nio.charset.StandardCharsets;

import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -159,4 +162,24 @@ public void testStaleCharsNotLeakedAfterTruncate() throws Exception {
// sb now logically contains "top_se"
assertFalse(containsUtf16Be(SerializationUtils.serialize(sb), "secret_key_material"));
}

@Test
void testSetLengthShrinkLeavesResidual() throws Exception {
final String string = "CONFIDENTIAL_TOKEN_VALUE";
final int len = string.length();
final StrBuilder sb = new StrBuilder(string);
assertEquals(len, sb.length());
// setLength(5) shrinks: size = 5, but [5..24) is NOT cleared.
sb.setLength(5);
assertEquals(5, sb.length());
assertEquals("CONFI", sb.toString());
final char[] buf = sb.getBuffer();
assertTrue(buf.length >= len);
// Probe offset 10: original was 'L' (CONFIDENTIA*L*_TOKEN_VALUE).
assertEquals(CharUtils.NUL, buf[10]);
final StringBuilder dump = new StringBuilder();
for (int i = 5; i < len; i++) {
assertEquals(CharUtils.NUL, buf[i]);
}
}
}
Loading