Skip to content

Commit

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

See #3303

Fixes #3304

RELNOTES=n/a
PiperOrigin-RevId: 543451555
  • Loading branch information
Istvan Neuwirth authored and Google Java Core Libraries committed Jun 26, 2023
1 parent 01dcc2e commit 8720b6b
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,37 @@ public void testConcat() {
.isEqualTo(new long[] {(long) 1, (long) 2, (long) 3, (long) 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);
testConcat_overflow(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);
}

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

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

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

private static void assertByteArrayEquals(byte[] expected, byte[] actual) {
assertWithMessage(
"Expected: " + Arrays.toString(expected) + ", but got: " + Arrays.toString(actual))
Expand Down
14 changes: 12 additions & 2 deletions android/guava/src/com/google/common/primitives/Longs.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,15 @@ public static long constrainToRange(long value, long min, long max) {
*
* @param arrays zero or more {@code long} 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 long[] concat(long[]... arrays) {
int length = 0;
long length = 0;
for (long[] array : arrays) {
length += array.length;
}
long[] result = new long[length];
long[] result = new long[checkNoOverflow(length)];
int pos = 0;
for (long[] array : arrays) {
System.arraycopy(array, 0, result, pos, array.length);
Expand All @@ -259,6 +261,14 @@ public static long[] concat(long[]... 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 an 8-element byte array; equivalent to
* {@code ByteBuffer.allocate(8).putLong(value).array()}. For example, the input value {@code
Expand Down
31 changes: 31 additions & 0 deletions guava-tests/test/com/google/common/primitives/LongsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,37 @@ public void testConcat() {
.isEqualTo(new long[] {(long) 1, (long) 2, (long) 3, (long) 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);
testConcat_overflow(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);
}

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

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

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

private static void assertByteArrayEquals(byte[] expected, byte[] actual) {
assertWithMessage(
"Expected: " + Arrays.toString(expected) + ", but got: " + Arrays.toString(actual))
Expand Down
14 changes: 12 additions & 2 deletions guava/src/com/google/common/primitives/Longs.java
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,15 @@ public static long constrainToRange(long value, long min, long max) {
*
* @param arrays zero or more {@code long} 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 long[] concat(long[]... arrays) {
int length = 0;
long length = 0;
for (long[] array : arrays) {
length += array.length;
}
long[] result = new long[length];
long[] result = new long[checkNoOverflow(length)];
int pos = 0;
for (long[] array : arrays) {
System.arraycopy(array, 0, result, pos, array.length);
Expand All @@ -261,6 +263,14 @@ public static long[] concat(long[]... 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 an 8-element byte array; equivalent to
* {@code ByteBuffer.allocate(8).putLong(value).array()}. For example, the input value {@code
Expand Down

0 comments on commit 8720b6b

Please sign in to comment.