sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 04/05: Tests against some integer overflows.
Date Sat, 09 Mar 2019 22:59:20 GMT
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 <martin.desruisseaux@geomatys.com>
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);
     }
 
     /**


Mime
View raw message