diff --git a/android/guava-tests/test/com/google/common/primitives/BooleansTest.java b/android/guava-tests/test/com/google/common/primitives/BooleansTest.java index e6fbd1aed90da..3d5c855590db0 100644 --- a/android/guava-tests/test/com/google/common/primitives/BooleansTest.java +++ b/android/guava-tests/test/com/google/common/primitives/BooleansTest.java @@ -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); + 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)); + + 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); diff --git a/android/guava-tests/test/com/google/common/primitives/BytesTest.java b/android/guava-tests/test/com/google/common/primitives/BytesTest.java index 59b5cded15d16..5e0962a81c91b 100644 --- a/android/guava-tests/test/com/google/common/primitives/BytesTest.java +++ b/android/guava-tests/test/com/google/common/primitives/BytesTest.java @@ -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); + 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)); + + 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); diff --git a/android/guava-tests/test/com/google/common/primitives/CharsTest.java b/android/guava-tests/test/com/google/common/primitives/CharsTest.java index a662adbb737b2..3191c1496fdc7 100644 --- a/android/guava-tests/test/com/google/common/primitives/CharsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/CharsTest.java @@ -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); + 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)); + + 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'); diff --git a/android/guava-tests/test/com/google/common/primitives/DoublesTest.java b/android/guava-tests/test/com/google/common/primitives/DoublesTest.java index 2c59eefb7e957..a00b3e07dd369 100644 --- a/android/guava-tests/test/com/google/common/primitives/DoublesTest.java +++ b/android/guava-tests/test/com/google/common/primitives/DoublesTest.java @@ -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); + 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)); + + 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); diff --git a/android/guava-tests/test/com/google/common/primitives/FloatsTest.java b/android/guava-tests/test/com/google/common/primitives/FloatsTest.java index 223bb7e1e555f..1bbaa062e4740 100644 --- a/android/guava-tests/test/com/google/common/primitives/FloatsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/FloatsTest.java @@ -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); + 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)); + + 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); diff --git a/android/guava-tests/test/com/google/common/primitives/IntsTest.java b/android/guava-tests/test/com/google/common/primitives/IntsTest.java index 85fb6b07e954a..b8629df3be5b3 100644 --- a/android/guava-tests/test/com/google/common/primitives/IntsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/IntsTest.java @@ -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); + 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)); + + 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)) diff --git a/android/guava-tests/test/com/google/common/primitives/ShortsTest.java b/android/guava-tests/test/com/google/common/primitives/ShortsTest.java index a3497fda368cb..bcd5b5502af25 100644 --- a/android/guava-tests/test/com/google/common/primitives/ShortsTest.java +++ b/android/guava-tests/test/com/google/common/primitives/ShortsTest.java @@ -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); + 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)); + + 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}); diff --git a/android/guava/src/com/google/common/primitives/Booleans.java b/android/guava/src/com/google/common/primitives/Booleans.java index 81a85adc6c259..c3180c7cd7b77 100644 --- a/android/guava/src/com/google/common/primitives/Booleans.java +++ b/android/guava/src/com/google/common/primitives/Booleans.java @@ -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); @@ -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}, diff --git a/android/guava/src/com/google/common/primitives/Bytes.java b/android/guava/src/com/google/common/primitives/Bytes.java index 9bc30ebc25849..7b2354efeb7b2 100644 --- a/android/guava/src/com/google/common/primitives/Bytes.java +++ b/android/guava/src/com/google/common/primitives/Bytes.java @@ -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); @@ -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}, diff --git a/android/guava/src/com/google/common/primitives/Chars.java b/android/guava/src/com/google/common/primitives/Chars.java index 9b91cdadc070a..bf1547f45f53d 100644 --- a/android/guava/src/com/google/common/primitives/Chars.java +++ b/android/guava/src/com/google/common/primitives/Chars.java @@ -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); @@ -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 diff --git a/android/guava/src/com/google/common/primitives/Doubles.java b/android/guava/src/com/google/common/primitives/Doubles.java index 540f6373492e1..219c566bc5ca6 100644 --- a/android/guava/src/com/google/common/primitives/Doubles.java +++ b/android/guava/src/com/google/common/primitives/Doubles.java @@ -271,13 +271,15 @@ public static double constrainToRange(double value, double min, double max) { * * @param arrays zero or more {@code double} 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 double[] concat(double[]... arrays) { - int length = 0; + long length = 0; for (double[] array : arrays) { length += array.length; } - double[] result = new double[length]; + double[] result = new double[checkNoOverflow(length)]; int pos = 0; for (double[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -286,6 +288,14 @@ public static double[] concat(double[]... 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; + } + private static final class DoubleConverter extends Converter implements Serializable { static final Converter INSTANCE = new DoubleConverter(); diff --git a/android/guava/src/com/google/common/primitives/Floats.java b/android/guava/src/com/google/common/primitives/Floats.java index ff2a25a7207fc..b42e62f4412cc 100644 --- a/android/guava/src/com/google/common/primitives/Floats.java +++ b/android/guava/src/com/google/common/primitives/Floats.java @@ -268,13 +268,15 @@ public static float constrainToRange(float value, float min, float max) { * * @param arrays zero or more {@code float} 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 float[] concat(float[]... arrays) { - int length = 0; + long length = 0; for (float[] array : arrays) { length += array.length; } - float[] result = new float[length]; + float[] result = new float[checkNoOverflow(length)]; int pos = 0; for (float[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -283,6 +285,14 @@ public static float[] concat(float[]... 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; + } + private static final class FloatConverter extends Converter implements Serializable { static final Converter INSTANCE = new FloatConverter(); diff --git a/android/guava/src/com/google/common/primitives/Ints.java b/android/guava/src/com/google/common/primitives/Ints.java index c0b1ff28079f1..6b5222ba7ae05 100644 --- a/android/guava/src/com/google/common/primitives/Ints.java +++ b/android/guava/src/com/google/common/primitives/Ints.java @@ -279,13 +279,15 @@ public static int constrainToRange(int value, int min, int max) { * * @param arrays zero or more {@code int} 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 int[] concat(int[]... arrays) { - int length = 0; + long length = 0; for (int[] array : arrays) { length += array.length; } - int[] result = new int[length]; + int[] result = new int[checkNoOverflow(length)]; int pos = 0; for (int[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -294,6 +296,14 @@ public static int[] concat(int[]... 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 4-element byte array; equivalent to * {@code ByteBuffer.allocate(4).putInt(value).array()}. For example, the input value {@code diff --git a/android/guava/src/com/google/common/primitives/Shorts.java b/android/guava/src/com/google/common/primitives/Shorts.java index fef9a0d9fad0d..43730a2823b3c 100644 --- a/android/guava/src/com/google/common/primitives/Shorts.java +++ b/android/guava/src/com/google/common/primitives/Shorts.java @@ -279,13 +279,15 @@ public static short constrainToRange(short value, short min, short max) { * * @param arrays zero or more {@code short} 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 short[] concat(short[]... arrays) { - int length = 0; + long length = 0; for (short[] array : arrays) { length += array.length; } - short[] result = new short[length]; + short[] result = new short[checkNoOverflow(length)]; int pos = 0; for (short[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -294,6 +296,14 @@ public static short[] concat(short[]... 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).putShort(value).array()}. For example, the input value {@code diff --git a/guava-tests/test/com/google/common/primitives/BooleansTest.java b/guava-tests/test/com/google/common/primitives/BooleansTest.java index e6fbd1aed90da..3d5c855590db0 100644 --- a/guava-tests/test/com/google/common/primitives/BooleansTest.java +++ b/guava-tests/test/com/google/common/primitives/BooleansTest.java @@ -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); + 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)); + + 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); diff --git a/guava-tests/test/com/google/common/primitives/BytesTest.java b/guava-tests/test/com/google/common/primitives/BytesTest.java index 59b5cded15d16..5e0962a81c91b 100644 --- a/guava-tests/test/com/google/common/primitives/BytesTest.java +++ b/guava-tests/test/com/google/common/primitives/BytesTest.java @@ -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); + 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)); + + 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); diff --git a/guava-tests/test/com/google/common/primitives/CharsTest.java b/guava-tests/test/com/google/common/primitives/CharsTest.java index a662adbb737b2..3191c1496fdc7 100644 --- a/guava-tests/test/com/google/common/primitives/CharsTest.java +++ b/guava-tests/test/com/google/common/primitives/CharsTest.java @@ -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); + 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)); + + 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'); diff --git a/guava-tests/test/com/google/common/primitives/DoublesTest.java b/guava-tests/test/com/google/common/primitives/DoublesTest.java index 2c59eefb7e957..a00b3e07dd369 100644 --- a/guava-tests/test/com/google/common/primitives/DoublesTest.java +++ b/guava-tests/test/com/google/common/primitives/DoublesTest.java @@ -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); + 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)); + + 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); diff --git a/guava-tests/test/com/google/common/primitives/FloatsTest.java b/guava-tests/test/com/google/common/primitives/FloatsTest.java index 223bb7e1e555f..1bbaa062e4740 100644 --- a/guava-tests/test/com/google/common/primitives/FloatsTest.java +++ b/guava-tests/test/com/google/common/primitives/FloatsTest.java @@ -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); + 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)); + + 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); diff --git a/guava-tests/test/com/google/common/primitives/IntsTest.java b/guava-tests/test/com/google/common/primitives/IntsTest.java index 85fb6b07e954a..b8629df3be5b3 100644 --- a/guava-tests/test/com/google/common/primitives/IntsTest.java +++ b/guava-tests/test/com/google/common/primitives/IntsTest.java @@ -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); + 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)); + + 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)) diff --git a/guava-tests/test/com/google/common/primitives/ShortsTest.java b/guava-tests/test/com/google/common/primitives/ShortsTest.java index a3497fda368cb..bcd5b5502af25 100644 --- a/guava-tests/test/com/google/common/primitives/ShortsTest.java +++ b/guava-tests/test/com/google/common/primitives/ShortsTest.java @@ -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); + 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)); + + 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}); diff --git a/guava/src/com/google/common/primitives/Booleans.java b/guava/src/com/google/common/primitives/Booleans.java index 81a85adc6c259..c3180c7cd7b77 100644 --- a/guava/src/com/google/common/primitives/Booleans.java +++ b/guava/src/com/google/common/primitives/Booleans.java @@ -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); @@ -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}, diff --git a/guava/src/com/google/common/primitives/Bytes.java b/guava/src/com/google/common/primitives/Bytes.java index 9bc30ebc25849..7b2354efeb7b2 100644 --- a/guava/src/com/google/common/primitives/Bytes.java +++ b/guava/src/com/google/common/primitives/Bytes.java @@ -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); @@ -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}, diff --git a/guava/src/com/google/common/primitives/Chars.java b/guava/src/com/google/common/primitives/Chars.java index 9b91cdadc070a..bf1547f45f53d 100644 --- a/guava/src/com/google/common/primitives/Chars.java +++ b/guava/src/com/google/common/primitives/Chars.java @@ -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); @@ -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 diff --git a/guava/src/com/google/common/primitives/Doubles.java b/guava/src/com/google/common/primitives/Doubles.java index f0e62035b3f19..6a666cff845dc 100644 --- a/guava/src/com/google/common/primitives/Doubles.java +++ b/guava/src/com/google/common/primitives/Doubles.java @@ -273,13 +273,15 @@ public static double constrainToRange(double value, double min, double max) { * * @param arrays zero or more {@code double} 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 double[] concat(double[]... arrays) { - int length = 0; + long length = 0; for (double[] array : arrays) { length += array.length; } - double[] result = new double[length]; + double[] result = new double[checkNoOverflow(length)]; int pos = 0; for (double[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -288,6 +290,14 @@ public static double[] concat(double[]... 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; + } + private static final class DoubleConverter extends Converter implements Serializable { static final Converter INSTANCE = new DoubleConverter(); diff --git a/guava/src/com/google/common/primitives/Floats.java b/guava/src/com/google/common/primitives/Floats.java index ff2a25a7207fc..b42e62f4412cc 100644 --- a/guava/src/com/google/common/primitives/Floats.java +++ b/guava/src/com/google/common/primitives/Floats.java @@ -268,13 +268,15 @@ public static float constrainToRange(float value, float min, float max) { * * @param arrays zero or more {@code float} 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 float[] concat(float[]... arrays) { - int length = 0; + long length = 0; for (float[] array : arrays) { length += array.length; } - float[] result = new float[length]; + float[] result = new float[checkNoOverflow(length)]; int pos = 0; for (float[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -283,6 +285,14 @@ public static float[] concat(float[]... 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; + } + private static final class FloatConverter extends Converter implements Serializable { static final Converter INSTANCE = new FloatConverter(); diff --git a/guava/src/com/google/common/primitives/Ints.java b/guava/src/com/google/common/primitives/Ints.java index 02b520cc0f403..584e3ffdc7194 100644 --- a/guava/src/com/google/common/primitives/Ints.java +++ b/guava/src/com/google/common/primitives/Ints.java @@ -281,13 +281,15 @@ public static int constrainToRange(int value, int min, int max) { * * @param arrays zero or more {@code int} 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 int[] concat(int[]... arrays) { - int length = 0; + long length = 0; for (int[] array : arrays) { length += array.length; } - int[] result = new int[length]; + int[] result = new int[checkNoOverflow(length)]; int pos = 0; for (int[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -296,6 +298,14 @@ public static int[] concat(int[]... 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 4-element byte array; equivalent to * {@code ByteBuffer.allocate(4).putInt(value).array()}. For example, the input value {@code diff --git a/guava/src/com/google/common/primitives/Shorts.java b/guava/src/com/google/common/primitives/Shorts.java index fef9a0d9fad0d..43730a2823b3c 100644 --- a/guava/src/com/google/common/primitives/Shorts.java +++ b/guava/src/com/google/common/primitives/Shorts.java @@ -279,13 +279,15 @@ public static short constrainToRange(short value, short min, short max) { * * @param arrays zero or more {@code short} 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 short[] concat(short[]... arrays) { - int length = 0; + long length = 0; for (short[] array : arrays) { length += array.length; } - short[] result = new short[length]; + short[] result = new short[checkNoOverflow(length)]; int pos = 0; for (short[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); @@ -294,6 +296,14 @@ public static short[] concat(short[]... 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).putShort(value).array()}. For example, the input value {@code