Skip to content

Commit

Permalink
Changed *.concat() to throw IllegalArgumentException if the input…
Browse files Browse the repository at this point in the history
… arrays contain too many elements.

Fixes #7376
Fixes #3303

This change comes from @perceptron8, who generalized the earlier PR by @ineuwirth.

RELNOTES=n/a
PiperOrigin-RevId: 668024782
  • Loading branch information
Istvan Neuwirth authored and Google Java Core Libraries committed Aug 27, 2024
1 parent 137798d commit 508cef7
Show file tree
Hide file tree
Showing 30 changed files with 608 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,37 @@ public void testConcat() {
.isEqualTo(new boolean[] {false, false, true});
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_negative() {
int dim1 = 1 << 16;
int dim2 = 1 << 15;
assertThat(dim1 * dim2).isLessThan(0);
testConcatOverflow(dim1, dim2);
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_nonNegative() {
int dim1 = 1 << 16;
int dim2 = 1 << 16;
assertThat(dim1 * dim2).isAtLeast(0);
testConcatOverflow(dim1, dim2);
}

private static void testConcatOverflow(int arraysDim1, int arraysDim2) {
assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2));

boolean[][] arrays = new boolean[arraysDim1][];
// it's shared to avoid using too much memory in tests
boolean[] sharedArray = new boolean[arraysDim2];
Arrays.fill(arrays, sharedArray);

try {
Booleans.concat(arrays);
fail();
} catch (IllegalArgumentException expected) {
}
}

public void testEnsureCapacity() {
assertThat(Booleans.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY);
assertThat(Booleans.ensureCapacity(ARRAY_FALSE, 0, 1)).isSameInstanceAs(ARRAY_FALSE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,37 @@ public void testConcat() {
.isEqualTo(new byte[] {(byte) 1, (byte) 2, (byte) 3, (byte) 4});
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_negative() {
int dim1 = 1 << 16;
int dim2 = 1 << 15;
assertThat(dim1 * dim2).isLessThan(0);
testConcatOverflow(dim1, dim2);
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_nonNegative() {
int dim1 = 1 << 16;
int dim2 = 1 << 16;
assertThat(dim1 * dim2).isAtLeast(0);
testConcatOverflow(dim1, dim2);
}

private static void testConcatOverflow(int arraysDim1, int arraysDim2) {
assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2));

byte[][] arrays = new byte[arraysDim1][];
// it's shared to avoid using too much memory in tests
byte[] sharedArray = new byte[arraysDim2];
Arrays.fill(arrays, sharedArray);

try {
Bytes.concat(arrays);
fail();
} catch (IllegalArgumentException expected) {
}
}

public void testEnsureCapacity() {
assertThat(Bytes.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY);
assertThat(Bytes.ensureCapacity(ARRAY1, 0, 1)).isSameInstanceAs(ARRAY1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,37 @@ public void testConcat() {
.isEqualTo(new char[] {(char) 1, (char) 2, (char) 3, (char) 4});
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_negative() {
int dim1 = 1 << 16;
int dim2 = 1 << 15;
assertThat(dim1 * dim2).isLessThan(0);
testConcatOverflow(dim1, dim2);
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_nonNegative() {
int dim1 = 1 << 16;
int dim2 = 1 << 16;
assertThat(dim1 * dim2).isAtLeast(0);
testConcatOverflow(dim1, dim2);
}

private static void testConcatOverflow(int arraysDim1, int arraysDim2) {
assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2));

char[][] arrays = new char[arraysDim1][];
// it's shared to avoid using too much memory in tests
char[] sharedArray = new char[arraysDim2];
Arrays.fill(arrays, sharedArray);

try {
Chars.concat(arrays);
fail();
} catch (IllegalArgumentException expected) {
}
}

@GwtIncompatible // Chars.fromByteArray
public void testFromByteArray() {
assertThat(Chars.fromByteArray(new byte[] {0x23, 0x45, (byte) 0xDC})).isEqualTo('\u2345');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,37 @@ public void testConcat() {
.isEqualTo(new double[] {(double) 1, (double) 2, (double) 3, (double) 4});
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_negative() {
int dim1 = 1 << 16;
int dim2 = 1 << 15;
assertThat(dim1 * dim2).isLessThan(0);
testConcatOverflow(dim1, dim2);
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_nonNegative() {
int dim1 = 1 << 16;
int dim2 = 1 << 16;
assertThat(dim1 * dim2).isAtLeast(0);
testConcatOverflow(dim1, dim2);
}

private static void testConcatOverflow(int arraysDim1, int arraysDim2) {
assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2));

double[][] arrays = new double[arraysDim1][];
// it's shared to avoid using too much memory in tests
double[] sharedArray = new double[arraysDim2];
Arrays.fill(arrays, sharedArray);

try {
Doubles.concat(arrays);
fail();
} catch (IllegalArgumentException expected) {
}
}

public void testEnsureCapacity() {
assertThat(Doubles.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY);
assertThat(Doubles.ensureCapacity(ARRAY1, 0, 1)).isSameInstanceAs(ARRAY1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,37 @@ public void testConcat() {
.isEqualTo(new float[] {(float) 1, (float) 2, (float) 3, (float) 4});
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_negative() {
int dim1 = 1 << 16;
int dim2 = 1 << 15;
assertThat(dim1 * dim2).isLessThan(0);
testConcatOverflow(dim1, dim2);
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_nonNegative() {
int dim1 = 1 << 16;
int dim2 = 1 << 16;
assertThat(dim1 * dim2).isAtLeast(0);
testConcatOverflow(dim1, dim2);
}

private static void testConcatOverflow(int arraysDim1, int arraysDim2) {
assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2));

float[][] arrays = new float[arraysDim1][];
// it's shared to avoid using too much memory in tests
float[] sharedArray = new float[arraysDim2];
Arrays.fill(arrays, sharedArray);

try {
Floats.concat(arrays);
fail();
} catch (IllegalArgumentException expected) {
}
}

public void testEnsureCapacity() {
assertThat(Floats.ensureCapacity(EMPTY, 0, 1)).isSameInstanceAs(EMPTY);
assertThat(Floats.ensureCapacity(ARRAY1, 0, 1)).isSameInstanceAs(ARRAY1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,37 @@ public void testConcat() {
.isEqualTo(new int[] {(int) 1, (int) 2, (int) 3, (int) 4});
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_negative() {
int dim1 = 1 << 16;
int dim2 = 1 << 15;
assertThat(dim1 * dim2).isLessThan(0);
testConcatOverflow(dim1, dim2);
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_nonNegative() {
int dim1 = 1 << 16;
int dim2 = 1 << 16;
assertThat(dim1 * dim2).isAtLeast(0);
testConcatOverflow(dim1, dim2);
}

private static void testConcatOverflow(int arraysDim1, int arraysDim2) {
assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2));

int[][] arrays = new int[arraysDim1][];
// it's shared to avoid using too much memory in tests
int[] sharedArray = new int[arraysDim2];
Arrays.fill(arrays, sharedArray);

try {
Ints.concat(arrays);
fail();
} catch (IllegalArgumentException expected) {
}
}

public void testToByteArray() {
assertThat(Ints.toByteArray(0x12131415)).isEqualTo(new byte[] {0x12, 0x13, 0x14, 0x15});
assertThat(Ints.toByteArray(0xFFEEDDCC))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,18 +201,18 @@ public void testConcat_overflow_negative() {
int dim1 = 1 << 16;
int dim2 = 1 << 15;
assertThat(dim1 * dim2).isLessThan(0);
testConcat_overflow(dim1, dim2);
testConcatOverflow(dim1, dim2);
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_nonNegative() {
int dim1 = 1 << 16;
int dim2 = 1 << 16;
assertThat(dim1 * dim2).isAtLeast(0);
testConcat_overflow(dim1, dim2);
testConcatOverflow(dim1, dim2);
}

private static void testConcat_overflow(int arraysDim1, int arraysDim2) {
private static void testConcatOverflow(int arraysDim1, int arraysDim2) {
assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2));

long[][] arrays = new long[arraysDim1][];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,37 @@ public void testConcat() {
.isEqualTo(new short[] {(short) 1, (short) 2, (short) 3, (short) 4});
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_negative() {
int dim1 = 1 << 16;
int dim2 = 1 << 15;
assertThat(dim1 * dim2).isLessThan(0);
testConcatOverflow(dim1, dim2);
}

@GwtIncompatible // different overflow behavior; could probably be made to work by using ~~
public void testConcat_overflow_nonNegative() {
int dim1 = 1 << 16;
int dim2 = 1 << 16;
assertThat(dim1 * dim2).isAtLeast(0);
testConcatOverflow(dim1, dim2);
}

private static void testConcatOverflow(int arraysDim1, int arraysDim2) {
assertThat((long) arraysDim1 * arraysDim2).isNotEqualTo((long) (arraysDim1 * arraysDim2));

short[][] arrays = new short[arraysDim1][];
// it's shared to avoid using too much memory in tests
short[] sharedArray = new short[arraysDim2];
Arrays.fill(arrays, sharedArray);

try {
Shorts.concat(arrays);
fail();
} catch (IllegalArgumentException expected) {
}
}

@GwtIncompatible // Shorts.toByteArray
public void testToByteArray() {
assertThat(Shorts.toByteArray((short) 0x2345)).isEqualTo(new byte[] {0x23, 0x45});
Expand Down
14 changes: 12 additions & 2 deletions android/guava/src/com/google/common/primitives/Booleans.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,15 @@ private static int lastIndexOf(boolean[] array, boolean target, int start, int e
*
* @param arrays zero or more {@code boolean} arrays
* @return a single array containing all the values from the source arrays, in order
* @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit
* in an {@code int}
*/
public static boolean[] concat(boolean[]... arrays) {
int length = 0;
long length = 0;
for (boolean[] array : arrays) {
length += array.length;
}
boolean[] result = new boolean[length];
boolean[] result = new boolean[checkNoOverflow(length)];
int pos = 0;
for (boolean[] array : arrays) {
System.arraycopy(array, 0, result, pos, array.length);
Expand All @@ -245,6 +247,14 @@ public static boolean[] concat(boolean[]... arrays) {
return result;
}

private static int checkNoOverflow(long result) {
checkArgument(
result == (int) result,
"the total number of elements (%s) in the arrays must fit in an int",
result);
return (int) result;
}

/**
* Returns an array containing the same values as {@code array}, but guaranteed to be of a
* specified minimum length. If {@code array} already has a length of at least {@code minLength},
Expand Down
14 changes: 12 additions & 2 deletions android/guava/src/com/google/common/primitives/Bytes.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,15 @@ private static int lastIndexOf(byte[] array, byte target, int start, int end) {
*
* @param arrays zero or more {@code byte} arrays
* @return a single array containing all the values from the source arrays, in order
* @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit
* in an {@code int}
*/
public static byte[] concat(byte[]... arrays) {
int length = 0;
long length = 0;
for (byte[] array : arrays) {
length += array.length;
}
byte[] result = new byte[length];
byte[] result = new byte[checkNoOverflow(length)];
int pos = 0;
for (byte[] array : arrays) {
System.arraycopy(array, 0, result, pos, array.length);
Expand All @@ -171,6 +173,14 @@ public static byte[] concat(byte[]... arrays) {
return result;
}

private static int checkNoOverflow(long result) {
checkArgument(
result == (int) result,
"the total number of elements (%s) in the arrays must fit in an int",
result);
return (int) result;
}

/**
* Returns an array containing the same values as {@code array}, but guaranteed to be of a
* specified minimum length. If {@code array} already has a length of at least {@code minLength},
Expand Down
14 changes: 12 additions & 2 deletions android/guava/src/com/google/common/primitives/Chars.java
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,15 @@ public static char constrainToRange(char value, char min, char max) {
*
* @param arrays zero or more {@code char} arrays
* @return a single array containing all the values from the source arrays, in order
* @throws IllegalArgumentException if the total number of elements in {@code arrays} does not fit
* in an {@code int}
*/
public static char[] concat(char[]... arrays) {
int length = 0;
long length = 0;
for (char[] array : arrays) {
length += array.length;
}
char[] result = new char[length];
char[] result = new char[checkNoOverflow(length)];
int pos = 0;
for (char[] array : arrays) {
System.arraycopy(array, 0, result, pos, array.length);
Expand All @@ -285,6 +287,14 @@ public static char[] concat(char[]... arrays) {
return result;
}

private static int checkNoOverflow(long result) {
checkArgument(
result == (int) result,
"the total number of elements (%s) in the arrays must fit in an int",
result);
return (int) result;
}

/**
* Returns a big-endian representation of {@code value} in a 2-element byte array; equivalent to
* {@code ByteBuffer.allocate(2).putChar(value).array()}. For example, the input value {@code
Expand Down
Loading

0 comments on commit 508cef7

Please sign in to comment.