8255150: Add utility methods to check long indexes and ranges

Co-authored-by: Paul Sandoz <psandoz@openjdk.org>
Reviewed-by: jvernee, dlong, vlivanov
This commit is contained in:
Roland Westrelin 2020-11-17 10:37:27 +00:00
parent 6d878565f8
commit a7422ac2f4
30 changed files with 893 additions and 51 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -68,4 +68,18 @@ public class IndexOutOfBoundsException extends RuntimeException {
public IndexOutOfBoundsException(int index) {
super("Index out of range: " + index);
}
/**
* Constructs a new {@code IndexOutOfBoundsException} class with an
* argument indicating the illegal index.
*
* <p>The index is included in this exception's detail message. The
* exact presentation format of the detail message is unspecified.
*
* @param index the illegal index.
* @since 16
*/
public IndexOutOfBoundsException(long index) {
super("Index out of range: " + index);
}
}

View file

@ -2146,7 +2146,7 @@ public abstract class VarHandle implements Constable {
UNSAFE.fullFence();
}
static final BiFunction<String, List<Integer>, ArrayIndexOutOfBoundsException>
static final BiFunction<String, List<Number>, ArrayIndexOutOfBoundsException>
AIOOBE_SUPPLIER = Preconditions.outOfBoundsExceptionFormatter(
new Function<String, ArrayIndexOutOfBoundsException>() {
@Override

View file

@ -411,4 +411,79 @@ public final class Objects {
return Preconditions.checkFromIndexSize(fromIndex, size, length, null);
}
/**
* Checks if the {@code index} is within the bounds of the range from
* {@code 0} (inclusive) to {@code length} (exclusive).
*
* <p>The {@code index} is defined to be out of bounds if any of the
* following inequalities is true:
* <ul>
* <li>{@code index < 0}</li>
* <li>{@code index >= length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* @param index the index
* @param length the upper-bound (exclusive) of the range
* @return {@code index} if it is within bounds of the range
* @throws IndexOutOfBoundsException if the {@code index} is out of bounds
* @since 16
*/
@ForceInline
public static
long checkIndex(long index, long length) {
return Preconditions.checkIndex(index, length, null);
}
/**
* Checks if the sub-range from {@code fromIndex} (inclusive) to
* {@code toIndex} (exclusive) is within the bounds of range from {@code 0}
* (inclusive) to {@code length} (exclusive).
*
* <p>The sub-range is defined to be out of bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code fromIndex > toIndex}</li>
* <li>{@code toIndex > length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* @param fromIndex the lower-bound (inclusive) of the sub-range
* @param toIndex the upper-bound (exclusive) of the sub-range
* @param length the upper-bound (exclusive) the range
* @return {@code fromIndex} if the sub-range within bounds of the range
* @throws IndexOutOfBoundsException if the sub-range is out of bounds
* @since 16
*/
public static
long checkFromToIndex(long fromIndex, long toIndex, long length) {
return Preconditions.checkFromToIndex(fromIndex, toIndex, length, null);
}
/**
* Checks if the sub-range from {@code fromIndex} (inclusive) to
* {@code fromIndex + size} (exclusive) is within the bounds of range from
* {@code 0} (inclusive) to {@code length} (exclusive).
*
* <p>The sub-range is defined to be out of bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code size < 0}</li>
* <li>{@code fromIndex + size > length}, taking into account integer overflow</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* @param fromIndex the lower-bound (inclusive) of the sub-interval
* @param size the size of the sub-range
* @param length the upper-bound (exclusive) of the range
* @return {@code fromIndex} if the sub-range within bounds of the range
* @throws IndexOutOfBoundsException if the sub-range is out of bounds
* @since 16
*/
public static
long checkFromIndexSize(long fromIndex, long size, long length) {
return Preconditions.checkFromIndexSize(fromIndex, size, length, null);
}
}

View file

@ -54,10 +54,10 @@ public class Preconditions {
* @return the runtime exception
*/
private static RuntimeException outOfBounds(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobef,
BiFunction<String, List<Number>, ? extends RuntimeException> oobef,
String checkKind,
Integer... args) {
List<Integer> largs = List.of(args);
Number... args) {
List<Number> largs = List.of(args);
RuntimeException e = oobef == null
? null : oobef.apply(checkKind, largs);
return e == null
@ -65,23 +65,41 @@ public class Preconditions {
}
private static RuntimeException outOfBoundsCheckIndex(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
int index, int length) {
return outOfBounds(oobe, "checkIndex", index, length);
}
private static RuntimeException outOfBoundsCheckFromToIndex(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
int fromIndex, int toIndex, int length) {
return outOfBounds(oobe, "checkFromToIndex", fromIndex, toIndex, length);
}
private static RuntimeException outOfBoundsCheckFromIndexSize(
BiFunction<String, List<Integer>, ? extends RuntimeException> oobe,
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
int fromIndex, int size, int length) {
return outOfBounds(oobe, "checkFromIndexSize", fromIndex, size, length);
}
private static RuntimeException outOfBoundsCheckIndex(
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
long index, long length) {
return outOfBounds(oobe, "checkIndex", index, length);
}
private static RuntimeException outOfBoundsCheckFromToIndex(
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
long fromIndex, long toIndex, long length) {
return outOfBounds(oobe, "checkFromToIndex", fromIndex, toIndex, length);
}
private static RuntimeException outOfBoundsCheckFromIndexSize(
BiFunction<String, List<Number>, ? extends RuntimeException> oobe,
long fromIndex, long size, long length) {
return outOfBounds(oobe, "checkFromIndexSize", fromIndex, size, length);
}
/**
* Returns an out-of-bounds exception formatter from an given exception
* factory. The exception formatter is a function that formats an
@ -90,8 +108,8 @@ public class Preconditions {
*
* <p>The exception formatter accepts two arguments: a {@code String}
* describing the out-of-bounds range check that failed, referred to as the
* <em>check kind</em>; and a {@code List<Integer>} containing the
* out-of-bound integer values that failed the check. The list of
* <em>check kind</em>; and a {@code List<Number>} containing the
* out-of-bound integral values that failed the check. The list of
* out-of-bound values is not modified.
*
* <p>Three check kinds are supported {@code checkIndex},
@ -102,7 +120,7 @@ public class Preconditions {
* {@link #checkFromToIndex(int, int, int, BiFunction) checkFromToIndex}, and
* {@link #checkFromIndexSize(int, int, int, BiFunction) checkFromIndexSize}.
* Thus a supported check kind corresponds to a method name and the
* out-of-bound integer values correspond to method argument values, in
* out-of-bound integral values correspond to method argument values, in
* order, preceding the exception formatter argument (similar in many
* respects to the form of arguments required for a reflective invocation of
* such a range check method).
@ -123,7 +141,7 @@ public class Preconditions {
* {@code static final} field as follows:
* <pre>{@code
* static final
* BiFunction<String, List<Integer>, ArrayIndexOutOfBoundsException> AIOOBEF =
* BiFunction<String, List<Number>, ArrayIndexOutOfBoundsException> AIOOBEF =
* outOfBoundsExceptionFormatter(ArrayIndexOutOfBoundsException::new);
* }</pre>
* The formatter instance {@code AIOOBEF} may be passed as an argument to an
@ -150,18 +168,18 @@ public class Preconditions {
* @return the out-of-bounds exception formatter
*/
public static <X extends RuntimeException>
BiFunction<String, List<Integer>, X> outOfBoundsExceptionFormatter(Function<String, X> f) {
BiFunction<String, List<Number>, X> outOfBoundsExceptionFormatter(Function<String, X> f) {
// Use anonymous class to avoid bootstrap issues if this method is
// used early in startup
return new BiFunction<String, List<Integer>, X>() {
return new BiFunction<String, List<Number>, X>() {
@Override
public X apply(String checkKind, List<Integer> args) {
public X apply(String checkKind, List<Number> args) {
return f.apply(outOfBoundsMessage(checkKind, args));
}
};
}
private static String outOfBoundsMessage(String checkKind, List<Integer> args) {
private static String outOfBoundsMessage(String checkKind, List<? extends Number> args) {
if (checkKind == null && args == null) {
return String.format("Range check failed");
} else if (checkKind == null) {
@ -213,7 +231,7 @@ public class Preconditions {
* <p>If the {@code index} is out of bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkIndex};
* and an unmodifiable list integers whose values are, in order, the
* and an unmodifiable list of integers whose values are, in order, the
* out-of-bounds arguments {@code index} and {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
@ -243,7 +261,7 @@ public class Preconditions {
@IntrinsicCandidate
public static <X extends RuntimeException>
int checkIndex(int index, int length,
BiFunction<String, List<Integer>, X> oobef) {
BiFunction<String, List<Number>, X> oobef) {
if (index < 0 || index >= length)
throw outOfBoundsCheckIndex(oobef, index, length);
return index;
@ -266,7 +284,7 @@ public class Preconditions {
* <p>If the sub-range is out of bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkFromToIndex};
* and an unmodifiable list integers whose values are, in order, the
* and an unmodifiable list of integers whose values are, in order, the
* out-of-bounds arguments {@code fromIndex}, {@code toIndex}, and {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
@ -290,7 +308,7 @@ public class Preconditions {
*/
public static <X extends RuntimeException>
int checkFromToIndex(int fromIndex, int toIndex, int length,
BiFunction<String, List<Integer>, X> oobef) {
BiFunction<String, List<Number>, X> oobef) {
if (fromIndex < 0 || fromIndex > toIndex || toIndex > length)
throw outOfBoundsCheckFromToIndex(oobef, fromIndex, toIndex, length);
return fromIndex;
@ -313,7 +331,7 @@ public class Preconditions {
* <p>If the sub-range is out of bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkFromIndexSize};
* and an unmodifiable list integers whose values are, in order, the
* and an unmodifiable list of integers whose values are, in order, the
* out-of-bounds arguments {@code fromIndex}, {@code size}, and
* {@code length}.
*
@ -338,7 +356,153 @@ public class Preconditions {
*/
public static <X extends RuntimeException>
int checkFromIndexSize(int fromIndex, int size, int length,
BiFunction<String, List<Integer>, X> oobef) {
BiFunction<String, List<Number>, X> oobef) {
if ((length | fromIndex | size) < 0 || size > length - fromIndex)
throw outOfBoundsCheckFromIndexSize(oobef, fromIndex, size, length);
return fromIndex;
}
/**
* Checks if the {@code index} is within the bounds of the range from
* {@code 0} (inclusive) to {@code length} (exclusive).
*
* <p>The {@code index} is defined to be out of bounds if any of the
* following inequalities is true:
* <ul>
* <li>{@code index < 0}</li>
* <li>{@code index >= length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>If the {@code index} is out of bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkIndex};
* and an unmodifiable list of longs whose values are, in order, the
* out-of-bounds arguments {@code index} and {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
* out of bounds
* @param index the index
* @param length the upper-bound (exclusive) of the range
* @param oobef the exception formatter that when applied with this
* method name and out-of-bounds arguments returns a runtime
* exception. If {@code null} or returns {@code null} then, it is as
* if an exception formatter produced from an invocation of
* {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used
* instead (though it may be more efficient).
* Exceptions thrown by the formatter are relayed to the caller.
* @return {@code index} if it is within bounds of the range
* @throws X if the {@code index} is out of bounds and the exception
* formatter is non-{@code null}
* @throws IndexOutOfBoundsException if the {@code index} is out of bounds
* and the exception formatter is {@code null}
* @since 16
*
* @implNote
* This method is made intrinsic in optimizing compilers to guide them to
* perform unsigned comparisons of the index and length when it is known the
* length is a non-negative value (such as that of an array length or from
* the upper bound of a loop)
*/
@IntrinsicCandidate
public static <X extends RuntimeException>
long checkIndex(long index, long length,
BiFunction<String, List<Number>, X> oobef) {
if (index < 0 || index >= length)
throw outOfBoundsCheckIndex(oobef, index, length);
return index;
}
/**
* Checks if the sub-range from {@code fromIndex} (inclusive) to
* {@code toIndex} (exclusive) is within the bounds of range from {@code 0}
* (inclusive) to {@code length} (exclusive).
*
* <p>The sub-range is defined to be out of bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code fromIndex > toIndex}</li>
* <li>{@code toIndex > length}</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>If the sub-range is out of bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkFromToIndex};
* and an unmodifiable list of longs whose values are, in order, the
* out-of-bounds arguments {@code fromIndex}, {@code toIndex}, and {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
* out of bounds
* @param fromIndex the lower-bound (inclusive) of the sub-range
* @param toIndex the upper-bound (exclusive) of the sub-range
* @param length the upper-bound (exclusive) the range
* @param oobef the exception formatter that when applied with this
* method name and out-of-bounds arguments returns a runtime
* exception. If {@code null} or returns {@code null} then, it is as
* if an exception formatter produced from an invocation of
* {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used
* instead (though it may be more efficient).
* Exceptions thrown by the formatter are relayed to the caller.
* @return {@code fromIndex} if the sub-range within bounds of the range
* @throws X if the sub-range is out of bounds and the exception factory
* function is non-{@code null}
* @throws IndexOutOfBoundsException if the sub-range is out of bounds and
* the exception factory function is {@code null}
* @since 16
*/
public static <X extends RuntimeException>
long checkFromToIndex(long fromIndex, long toIndex, long length,
BiFunction<String, List<Number>, X> oobef) {
if (fromIndex < 0 || fromIndex > toIndex || toIndex > length)
throw outOfBoundsCheckFromToIndex(oobef, fromIndex, toIndex, length);
return fromIndex;
}
/**
* Checks if the sub-range from {@code fromIndex} (inclusive) to
* {@code fromIndex + size} (exclusive) is within the bounds of range from
* {@code 0} (inclusive) to {@code length} (exclusive).
*
* <p>The sub-range is defined to be out of bounds if any of the following
* inequalities is true:
* <ul>
* <li>{@code fromIndex < 0}</li>
* <li>{@code size < 0}</li>
* <li>{@code fromIndex + size > length}, taking into account integer overflow</li>
* <li>{@code length < 0}, which is implied from the former inequalities</li>
* </ul>
*
* <p>If the sub-range is out of bounds, then a runtime exception is
* thrown that is the result of applying the following arguments to the
* exception formatter: the name of this method, {@code checkFromIndexSize};
* and an unmodifiable list of longs whose values are, in order, the
* out-of-bounds arguments {@code fromIndex}, {@code size}, and
* {@code length}.
*
* @param <X> the type of runtime exception to throw if the arguments are
* out of bounds
* @param fromIndex the lower-bound (inclusive) of the sub-interval
* @param size the size of the sub-range
* @param length the upper-bound (exclusive) of the range
* @param oobef the exception formatter that when applied with this
* method name and out-of-bounds arguments returns a runtime
* exception. If {@code null} or returns {@code null} then, it is as
* if an exception formatter produced from an invocation of
* {@code outOfBoundsExceptionFormatter(IndexOutOfBounds::new)} is used
* instead (though it may be more efficient).
* Exceptions thrown by the formatter are relayed to the caller.
* @return {@code fromIndex} if the sub-range within bounds of the range
* @throws X if the sub-range is out of bounds and the exception factory
* function is non-{@code null}
* @throws IndexOutOfBoundsException if the sub-range is out of bounds and
* the exception factory function is {@code null}
* @since 16
*/
public static <X extends RuntimeException>
long checkFromIndexSize(long fromIndex, long size, long length,
BiFunction<String, List<Number>, X> oobef) {
if ((length | fromIndex | size) < 0 || size > length - fromIndex)
throw outOfBoundsCheckFromIndexSize(oobef, fromIndex, size, length);
return fromIndex;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,7 +37,7 @@ import jdk.internal.util.Preconditions;
public final class ArrayUtil {
private static final BiFunction<String, List<Integer>,
private static final BiFunction<String, List<Number>,
ArrayIndexOutOfBoundsException> AIOOBE_SUPPLIER =
Preconditions.outOfBoundsExceptionFormatter
(ArrayIndexOutOfBoundsException::new);