JDK 16: Checking Indexes and Ranges of Longs

原文はこちら。
The original article was written by Dustin Marx.
http://marxsoftware.blogspot.com/2020/11/jdk16-check-long-indexes-ranges.html

以前、day periodがJDK 16 EA Build 25でサポートされたことを記載しましたが、同じビルドにlong値のインデックスやレンジのチェックのためのメソッドが追加されました。これがこのエントリの主題です。

Day Period Support in JDK 16
https://marxsoftware.blogspot.com/2020/11/day-period-support-in-jdk-16.html
https://logico-jp.io/2020/12/02/day-period-support-in-jdk-16/
JDK 16
https://jdk.java.net/16/

JDK-8255150はJDK-8135248がJDK 9でint型に対して追加されたものと類似した、longのindexとレンジをチェックするためのユーティリティメソッドを追加するための機能強化です。

[JDK/JDK-8255150] Add utility methods to check long indexes and ranges
https://bugs.openjdk.java.net/browse/JDK-8255150
[JDK/JDK-8135248] Add utility methods to check indexes and ranges
https://bugs.openjdk.java.net/browse/JDK-8135248

JDK-8255150では以下のような記載があります。

“The goal is to add a similar set of methods [as JDK-8135248] but rather than operate on int arguments, the new methods operate on long arguments.”
(このエンハンスメントのゴールは、JDK-8135248と類似の一連のメソッドを追加することだが、新しいメソッドはint引数ではなくlong引数を操作する。)

[JDK/JDK-8255150] Add utility methods to check long indexes and ranges
https://bugs.openjdk.java.net/browse/JDK-8255150

JDK-8255150では、Objectクラス(JDK-8135248に説明があります)に追加された3個の新しいメソッドのためのメソッドシグネチャをリストにしています。

Objects (Java SE 15 & JDK 15)
https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/util/Objects.html
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Objects.html

メソッド説明
public static long checkIndex(long index, long length)インデックスが範囲内にあるかどうかをチェックする
public static long checkFromToIndex(long fromIndex, long toIndex, long length)絶対範囲が範囲内にあるかどうかをチェックする
public static long checkFromIndexSize(long fromIndex, long size, long length)相対範囲が範囲内にあるかどうかをチェックする

これらの新しいメソッドがint用のユーティリティメソッドのミラーであるため、JDK-8135248を参照して、これらのメソッドの導入を正当化するための歴史的背景を確認するのは有用です。このエンハンスメントでは、以下のような記述があります。

“There are numerous methods in the JDK that check if an index or an absolute/relative range is valid before accessing the contents of an array (or in general a memory region for the case of a direct java.nio.ByteBuffer). … Such checks, while not difficult, are often easy to get wrong and optimize correctly, thus there is a risk to the integrity and security of the runtime.”
(配列や、java.nio.ByteBufferに直接アクセスする場合メモリ領域の内容にアクセスする前に、インデックスや絶対/相対範囲が有効かどうかをチェックするメソッドがJDKには数多く存在する。… (中略) … このようなチェックは難しくはないものの、多くの場合、取り違えたり正しく最適化したりすることも簡単ゆえ、ランタイムの整合性やセキュリティに対するリスクがある。)

[JDK/JDK-8255150] Add utility methods to check long indexes and ranges
https://bugs.openjdk.java.net/browse/JDK-8255150

JDK-8135248では次のような最適化の可能性についても述べています。

“A further desire for such methods is some or all can be made intrinsic (see JDK-8042997), thus hinting to the HotSpot runtime compiler to use unsigned comparisons and better optimize array access (via aaload/store or Unsafe) in loops (especially those that are unrolled).”
(こうしたメソッドに対する更なる要望は、一部またはすべてをintrinsicにすることである(JDK-8042997を参照)。これにより、HotSpotランタイムコンパイラに対して、符号なし比較を使用したり、ループ(特に展開されたもの)における(aaload/storeまたはUnsafeを使った)配列アクセスをより最適化したりすることを暗に求めている。)

[JDK/JDK-8255150] Add utility methods to check long indexes and ranges
https://bugs.openjdk.java.net/browse/JDK-8255150

[JDK/JDK-8042997] Make intrinsic some or all check index/range methods
https://bugs.openjdk.java.net/browse/JDK-8042997

LongIndexRangeChecksDemoという、このたび追加されたこれらのメソッドを説明するためのクラスはGitHubからご利用いただけます。

LongIndexRangeChecksDemo.java
https://github.com/dustinmarx/javademos/blob/master/src/dustin/examples/jdk16/check/LongIndexRangeChecksDemo.java
IndexOutOfBoundsException (Java SE 15 & JDK 15)
https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/lang/IndexOutOfBoundsException.html
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/IndexOutOfBoundsException.html

このクラスでは、提示されたインデックスやサイズ値が許可された範囲にないことを示すindexOutOfBoundsExceptionをスローする様々なチェックを説明しています。main(String[])関数は全ての例のメソッドを呼び出します。この出力を分解して、以下のセクションで説明します。

checkIndex Example Output

この例に関連するメッセージはインデックスが範囲外であり、どのように範囲外になっているのかを明確に示しています。

==========================
== checkIndex Exception ==
==========================
java.lang.<strong>IndexOutOfBoundsException: Index 7 out of bounds for length 5</strong>
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:88)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:412)
	at java.base/java.util.Objects.checkIndex(Objects.java:435)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.lambda$demoCheckIndexException$0(LongIndexRangeChecksDemo.java:34)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.executeDemonstration(LongIndexRangeChecksDemo.java:96)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.demoCheckIndexException(LongIndexRangeChecksDemo.java:33)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.main(LongIndexRangeChecksDemo.java:115)

checkFromToIndex Example Output

このメッセージは”from”と”to”のインデックスが指定する範囲が想定される長さの容量に比べて大きすぎることを明確に示しています。範囲の説明の先頭の”[“は閉区間の開始を、範囲の説明の末尾の”)“は開区間の終了を示している点に注意してください。を示していることに注意してください。

================================
== checkFromToIndex Exception ==
================================
java.lang.IndexOutOfBoundsException: Range [2, 6) out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:94)
	at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:459)
	at java.base/java.util.Objects.checkFromToIndex(Objects.java:461)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.lambda$demoCheckFromToIndexException$1(LongIndexRangeChecksDemo.java:48)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.executeDemonstration(LongIndexRangeChecksDemo.java:96)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.demoCheckFromToIndexException(LongIndexRangeChecksDemo.java:47)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.main(LongIndexRangeChecksDemo.java:116)

checkFromIndexSize Example Output

この例では、”from”インデックスと範囲のサイズで組成される範囲幅が、指定された長さの容量の範囲外であることを示しています。

==================================
== checkFromIndexSize Exception ==
==================================
java.lang.IndexOutOfBoundsException: Range [2, 2 + 6) out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromIndexSize(Preconditions.java:100)
	at java.base/jdk.internal.util.Preconditions.checkFromIndexSize(Preconditions.java:507)
	at java.base/java.util.Objects.checkFromIndexSize(Objects.java:487)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.lambda$demoCheckFromIndexSizeException$2(LongIndexRangeChecksDemo.java:62)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.executeDemonstration(LongIndexRangeChecksDemo.java:96)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.demoCheckFromIndexSizeException(LongIndexRangeChecksDemo.java:61)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.main(LongIndexRangeChecksDemo.java:117)

checkFromIndexSize Overflow Example Output

この例では、”from”インデックスと範囲のサイズで組成される範囲幅が、初期インデックスへのサイズ加算時に数値のオーバーフローが発生しているために、範囲外であることを示しています。これは素晴らしい例外の補足です。というのも、与えられた初期インデックスとサイズの両方が正の値であることをチェックした後に許容される長さに対するインデックスとサイズの合計をチェックするという、単純化されすぎた内製アプローチでは、オーバーフローの可能性があるためにロジックの欠陥の可能性があるためです。

=============================================
== checkFromIndexSize (Overflow) Exception ==
=============================================
java.lang.IndexOutOfBoundsException: Range [2, 2 + 9223372036854775807) out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromIndexSize(Preconditions.java:100)
	at java.base/jdk.internal.util.Preconditions.checkFromIndexSize(Preconditions.java:507)
	at java.base/java.util.Objects.checkFromIndexSize(Objects.java:487)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.lambda$demoCheckFromIndexSizeExceptionOnOverflow$3(LongIndexRangeChecksDemo.java:77)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.executeDemonstration(LongIndexRangeChecksDemo.java:96)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.demoCheckFromIndexSizeExceptionOnOverflow(LongIndexRangeChecksDemo.java:76)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.main(LongIndexRangeChecksDemo.java:118)

Common Uses

これらの新規追加されたlongをサポートするメソッドの最大の恩恵を受けるのは、メーリングリストの以下のメッセージにあるように、foreign memory access API(外部メモリアクセスAPI)の作者、メンテナ、そしてユーザーかもしれません。

“We have to jump through quite a few hoops in the implementation of the foreign memory access API in order to leverage the intrinsification of int-based index checks, and even then we are not covering the cases where the numbers are larger than ints. Looking forward to being able to remove those hacks!”
intベースのインデックスチェックの内在化を利用するためには、外部メモリアクセスAPIの実装において複雑な手順を踏む必要があり、その上、intの最大値を上回る数の場合をカバーできていない。これらの切れ目が取り除かれるなることを期待している。)

RFR: 8255150: Add utility methods to check long indexes and ranges
https://mail.openjdk.java.net/pipermail/core-libs-dev/2020-November/070882.html

JEP 393: Foreign-Memory Access API (Third Incubator)
https://openjdk.java.net/jeps/393
JEP 348: Compiler Intrinsics for Java SE APIs
https://openjdk.java.net/jeps/348

これらのメソッドは、checkIndex(int, int)checkFromToIndex(int, int, int)checkFromIndexSize(int, int, int)requireNonNull(T)requireNonNull(T, String)といった他のObjectsのメソッドと同様に、想定される前提条件に対してメソッドのパラメータをチェックするためのメソッドガードとして一般的に使用される可能性があります。

Objects (Java SE 15 & JDK 15)
https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/util/Objects.html
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Objects.html
checkIndex
https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/util/Objects.html#checkIndex(int,int)
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Objects.html#checkIndex(int,int)
checkFromToIndex
https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/util/Objects.html#checkFromToIndex(int,int,int)
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Objects.html#checkFromToIndex(int,int,int)
checkFromIndexSize
https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/util/Objects.html#checkFromIndexSize(int,int,int)
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Objects.html#checkFromIndexSize(int,int,int)
requireNonNull(T)
https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/util/Objects.html#requireNonNull(T)
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Objects.html#requireNonNull(T)
requireNonNull(T, String)
https://docs.oracle.com/javase/jp/15/docs/api/java.base/java/util/Objects.html#requireNonNull(T,java.lang.String)
https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Objects.html#requireNonNull(T,java.lang.String)

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中