This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 141af4766b5317672ee530c0ad1deb6ec6302611 Author: Martin Desruisseaux <[email protected]> AuthorDate: Fri Mar 8 15:06:54 2019 +0100 Tests against some integer overflows. --- .../org/apache/sis/coverage/grid/GridExtent.java | 4 +- .../main/java/org/apache/sis/math/ArrayVector.java | 8 +-- .../main/java/org/apache/sis/math/Fraction.java | 7 +- .../src/main/java/org/apache/sis/math/Vector.java | 2 +- .../main/java/org/apache/sis/util/ArraysExt.java | 82 ++++++++++++++++++++-- .../java/org/apache/sis/util/resources/Errors.java | 5 ++ .../apache/sis/util/resources/Errors.properties | 1 + .../apache/sis/util/resources/Errors_fr.properties | 1 + .../java/org/apache/sis/util/ArraysExtTest.java | 19 +++++ .../java/org/apache/sis/internal/netcdf/Axis.java | 2 +- .../org/apache/sis/internal/netcdf/CRSBuilder.java | 2 +- .../org/apache/sis/internal/netcdf/Variable.java | 11 ++- 12 files changed, 125 insertions(+), 19 deletions(-) diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java index d16598b..2dd3bf1 100644 --- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java +++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java @@ -995,7 +995,9 @@ public class GridExtent implements Serializable { final int j = i + m; long low = coordinates[i]; long size = coordinates[j] - low + 1; // Result is an unsigned number. - if (size == 0) throw new ArithmeticException("long overflow"); + if (size == 0) { + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE)); + } long r = Long.divideUnsigned(size, s); if (r*s == size) r--; // Make inclusive if the division did not already rounded toward 0. sub.coordinates[i] = low /= s; diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java b/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java index d5dd4a2..8f66146 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java +++ b/core/sis-utility/src/main/java/org/apache/sis/math/ArrayVector.java @@ -919,7 +919,7 @@ abstract class ArrayVector<E extends Number> extends Vector implements CheckedCo @Override public long longValue(final int index) { final long value = super.longValue(index); if (value >= 0) return value; - throw new ArithmeticException(); + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE)); } /** Returns the string representation at the given index. */ @@ -960,7 +960,7 @@ abstract class ArrayVector<E extends Number> extends Vector implements CheckedCo @Override public int intValue(int index) { final int value = super.intValue(index); if (value >= 0) return value; - throw new ArithmeticException(); + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Integer.SIZE)); } /** Uses a larger type if the value exceed integer capacity. */ @@ -1016,7 +1016,7 @@ abstract class ArrayVector<E extends Number> extends Vector implements CheckedCo @Override public short shortValue(int index) { final short value = super.shortValue(index); if (value >= 0) return value; - throw new ArithmeticException(); + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Short.SIZE)); } /** Uses a larger type if the value exceed short integer capacity. */ @@ -1073,7 +1073,7 @@ abstract class ArrayVector<E extends Number> extends Vector implements CheckedCo @Override public byte byteValue(int index) { final byte value = super.byteValue(index); if (value >= 0) return value; - throw new ArithmeticException(); + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Byte.SIZE)); } /** Uses a larger type if the value exceed integer capacity. */ diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java b/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java index 92846f5..e33eb30 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java +++ b/core/sis-utility/src/main/java/org/apache/sis/math/Fraction.java @@ -18,6 +18,7 @@ package org.apache.sis.math; import java.io.Serializable; import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.resources.Errors; import org.apache.sis.util.collection.WeakHashSet; @@ -115,7 +116,7 @@ public final class Fraction extends Number implements Comparable<Fraction>, Seri */ private Fraction simplify(long num, long den) { if (num == Long.MIN_VALUE || den == Long.MIN_VALUE) { - throw new ArithmeticException(); + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE)); } if (num == 0) { den = Long.signum(den); // Simplify 0/x as 0/±1 or 0/0. @@ -357,7 +358,7 @@ public final class Fraction extends Number implements Comparable<Fraction>, Seri public short shortValue() { final int n = intValue(); if ((n & ~0xFFFF) == 0) return (short) n; - throw new ArithmeticException(); + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Short.SIZE)); } /** @@ -370,7 +371,7 @@ public final class Fraction extends Number implements Comparable<Fraction>, Seri public byte byteValue() { final int n = intValue(); if ((n & ~0xFF) == 0) return (byte) n; - throw new ArithmeticException(); + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Byte.SIZE)); } /** diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java b/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java index f25ed0d..abce606 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java +++ b/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java @@ -687,7 +687,7 @@ search: for (;;) { final long inc = a - b; // The sign of the difference shall be the same than the sign of Long.compare(…). if ((((isUnsigned() ? Long.compareUnsigned(a, b) : Long.compare(a, b)) ^ inc) & Long.MIN_VALUE) != 0) { - throw new ArithmeticException(); + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Long.SIZE)); } return inc; } diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java b/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java index 51c80b0..a6dd7a7 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java +++ b/core/sis-utility/src/main/java/org/apache/sis/util/ArraysExt.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.Comparator; import java.lang.reflect.Array; +import org.apache.sis.util.resources.Errors; /** @@ -1258,22 +1259,89 @@ public final class ArraysExt extends Static { } /** - * Returns a sequence of increasing values of the given length. Each value is increased by 1. - * For example {@code sequence(-1, 4)} returns {@code {-1, 0, 1, 2}}. This method is a convenience for - * enumerating a subset of dimensions in a coordinate reference system or a subset of bands in an image. + * Returns a finite arithmetic progression of the given length. Each value is increased by 1. + * For example {@code sequence(-1, 4)} returns {@code {-1, 0, 1, 2}}. + * + * <div class="note"><b>Purpose:</b> + * this method is convenient for enumerating dimensions in a coordinate reference system or bands in an image. + * Some methods in the Java library or in Apache SIS want dimensions or bands to be specified by their indices. + * An example in the Java library is the {@code bankIndices} argument in + * <code>{@linkplain java.awt.image.Raster#createBandedRaster(int, int, int, int, int[], int[], java.awt.Point) + * Raster.createBandedRaster}(…, bankIndices, …)</code>. + * An example in Apache SIS is the {@code range} argument in + * <code>{@linkplain org.apache.sis.storage.GridCoverageResource#read GridCoverageResource.read}(…, range)</code>.</div> + * + * For any array returned by this method, <code>{@linkplain #isSequence(int, int[]) isSequence}(start, array)</code> + * is guaranteed to return {@code true}. * * @param start first value in the array to return. * @param length number of values to return. * @return a sequence of increasing integers starting at {@code start} and having {@code length} values. + * @throws ArithmeticException if {@code start + length} overflows 32 bits integer. + * + * @see java.util.stream.IntStream#range(int, int) * * @since 1.0 */ public static int[] sequence(final int start, final int length) { - final int[] array = new int[length]; - for (int i=0; i<length; i++) { - array[i] = start + i; + if (length > 0) { + if (start + (length - 1) >= start) { + final int[] array = new int[length]; + for (int i=0; i<length; i++) { + array[i] = start + i; + } + return array; + } else { + throw new ArithmeticException(Errors.format(Errors.Keys.IntegerOverflow_1, Integer.SIZE)); + } + } else if (length == 0) { + return EMPTY_INT; + } else { + throw new NegativeArraySizeException(Errors.format(Errors.Keys.NegativeArgument_2, "length", length)); } - return array; + } + + /** + * Returns {@code true} if the given array is a finite arithmetic progression starting at the given value. + * More specifically: + * + * <ul> + * <li>If {@code array} is {@code null}, then return {@code false}.</li> + * <li>Otherwise if {@code array} is empty, then return {@code true} for consistency + * with <code>{@linkplain #sequence(int, int) sequence}(start, 0)</code>.</li> + * <li>Otherwise for any index 0 ≦ <var>i</var> {@literal <} {@code array.length}, if {@code array[i]} + * is equal to {@code start + i} (computed as if no overflow occurs), then return {@code true}.</li> + * <li>Otherwise return {@code false}.</li> + * </ul> + * + * <div class="note"><b>Example:</b> + * {@code isSequence(1, array)} returns {@code true} if the given array is {@code {1, 2, 3, 4}} + * but {@code false} if the array is {@code {1, 2, 4}} (missing 3).</div> + * + * @param start first value expected in the given {@code array}. + * @param array the array to test, or {@code null}. + * @return {@code true} if the given array is non-null and equal to + * <code>{@linkplain #sequence(int, int) sequence}(start, array.length)</code>. + * + * @see #sequence(int, int) + * + * @since 1.0 + */ + public static boolean isSequence(final int start, final int[] array) { + if (array == null) { + return false; + } + if (array.length != 0) { + if (start + (array.length - 1) < start) { + return false; // Overflow. + } + for (int i=0; i<array.length; i++) { + if (array[i] != start + i) { + return false; + } + } + } + return true; } /** diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java index 7ca6176..52589b4 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java +++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java @@ -489,6 +489,11 @@ public final class Errors extends IndexedResourceBundle { public static final short InfiniteArgumentValue_1 = 73; /** + * Integer overflow during {0} bits arithmetic operation. + */ + public static final short IntegerOverflow_1 = 188; + + /** * “{0}” is an invalid version identifier. */ public static final short InvalidVersionIdentifier_1 = 179; diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties index 75be8f1..6007f78 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties +++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties @@ -108,6 +108,7 @@ InconsistentUnitsForCS_1 = Unit of measurement \u201c{0}\u201d is incon IndexOutOfBounds_1 = Index {0} is out of bounds. IndicesOutOfBounds_2 = Indices ({0}, {1}) are out of bounds. InfiniteArgumentValue_1 = Argument \u2018{0}\u2019 can not take an infinite value. +IntegerOverflow_1 = Integer overflow during {0} bits arithmetic operation. InvalidVersionIdentifier_1 = \u201c{0}\u201d is an invalid version identifier. KeyCollision_1 = Key \u201c{0}\u201d is associated twice to different values. MandatoryAttribute_2 = Attribute \u201c{0}\u201d is mandatory for an object of type \u2018{1}\u2019. diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties index f87e89b..fe58d55 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties +++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties @@ -105,6 +105,7 @@ InconsistentUnitsForCS_1 = L\u2019unit\u00e9 de mesure \u00ab\u202f{0}\ IndexOutOfBounds_1 = L\u2019index {0} est en dehors des limites permises. IndicesOutOfBounds_2 = Les index ({0}, {1}) sont en dehors des limites permises. InfiniteArgumentValue_1 = L\u2019argument \u2018{0}\u2019 ne peut pas prendre une valeur infinie. +IntegerOverflow_1 = D\u00e9passement d\u2019entier lors d\u2019une op\u00e9ration arithm\u00e9tique sur {0} bits. InvalidVersionIdentifier_1 = \u00ab\u202f{0}\u202f\u00bb n\u2019est pas un identifiant de version valide. KeyCollision_1 = La cl\u00e9 \u00ab\u202f{0}\u202f\u00bb est associ\u00e9e deux fois \u00e0 des valeurs diff\u00e9rentes. MandatoryAttribute_2 = L\u2019attribut \u00ab\u202f{0}\u202f\u00bb est obligatoire pour un objet de type \u2018{1}\u2019. diff --git a/core/sis-utility/src/test/java/org/apache/sis/util/ArraysExtTest.java b/core/sis-utility/src/test/java/org/apache/sis/util/ArraysExtTest.java index 295eb80..df015a2 100644 --- a/core/sis-utility/src/test/java/org/apache/sis/util/ArraysExtTest.java +++ b/core/sis-utility/src/test/java/org/apache/sis/util/ArraysExtTest.java @@ -58,6 +58,25 @@ public final strictfp class ArraysExtTest extends TestCase { } /** + * Tests {@link ArraysExt#sequence(int, int)} and {@link ArraysExt#isSequence(int, int[])}. + */ + @Test + public void testSequence() { + int[] sequence = ArraysExt.sequence(-1, 4); + assertArrayEquals("sequence", new int[] {-1, 0, 1, 2}, sequence); + assertTrue ("isSequence", ArraysExt.isSequence(-1, sequence)); + assertFalse("isSequence", ArraysExt.isSequence(-2, sequence)); + assertTrue ("isSequence", ArraysExt.isSequence(1, new int[] {1, 2, 3, 4})); + assertFalse("isSequence", ArraysExt.isSequence(1, new int[] {1, 2, 4})); + try { + ArraysExt.sequence(Integer.MAX_VALUE - 10, 12); + fail("Expected ArithmeticException."); + } catch (ArithmeticException e) { + assertNotNull(e.getMessage()); + } + } + + /** * Tests {@link ArraysExt#unionOfSorted(int[], int[])}. */ @Test diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java index 84879da..9f24ba7 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java @@ -349,7 +349,7 @@ public final class Axis extends NamedElement { private int getSize(final int i) { final int n = sourceSizes[i]; if (n >= 0) return n; - throw new ArithmeticException("signed integer overflow"); + throw new ArithmeticException(coordinates.errors().getString(Errors.Keys.IntegerOverflow_1, Integer.SIZE)); } /** diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java index a2367f4..4c2579a 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java @@ -235,7 +235,7 @@ previous: for (int i=components.size(); --i >= 0;) { */ private void add(final Axis axis) throws DataStoreContentException { if (dimension == Byte.MAX_VALUE) { - throw new DataStoreContentException(Errors.getResources(getFirstAxis().coordinates.getLocale()) + throw new DataStoreContentException(getFirstAxis().coordinates.errors() .getString(Errors.Keys.ExcessiveListSize_2, "axes", (short) (Byte.MAX_VALUE + 1))); } if (dimension >= axes.length) { diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java index 1438698..19f7d4a 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java @@ -882,6 +882,15 @@ public abstract class Variable extends NamedElement { } /** + * Returns the resources to use for error messages. + * + * @return the resources for error messages using the locales specified to the decoder. + */ + final Errors errors() { + return Errors.getResources(getLocale()); + } + + /** * Reports a warning to the listeners specified at construction time. * This method is for Apache SIS internal purpose only since resources may change at any time. * @@ -904,7 +913,7 @@ public abstract class Variable extends NamedElement { * @param arguments values to be formatted in the {@link java.text.MessageFormat} pattern. */ final void error(final Class<?> caller, final String method, final Exception exception, final short key, final Object... arguments) { - warning(decoder.listeners, caller, method, exception, Errors.getResources(getLocale()), key, arguments); + warning(decoder.listeners, caller, method, exception, errors(), key, arguments); } /**
