原文はこちら。
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
[JDK/JDK-8255150] Add utility methods to check long indexes and rangesint
arguments, the new methods operate onlong
arguments.”
(このエンハンスメントのゴールは、JDK-8135248と類似の一連のメソッドを追加することだが、新しいメソッドはint
引数ではなくlong
引数を操作する。)
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
[JDK/JDK-8255150] Add utility methods to check long indexes and rangesjava.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には数多く存在する。… (中略) … このようなチェックは難しくはないものの、多くの場合、取り違えたり正しく最適化したりすることも簡単ゆえ、ランタイムの整合性やセキュリティに対するリスクがある。)
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/JDK-8255150] Add utility methods to check long indexes and rangesJDK-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を使った)配列アクセスをより最適化したりすることを暗に求めている。)
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
RFR: 8255150: Add utility methods to check long indexes and rangesint
-based index checks, and even then we are not covering the cases where the numbers are larger thanint
s. Looking forward to being able to remove those hacks!”
(int
ベースのインデックスチェックの内在化を利用するためには、外部メモリアクセスAPIの実装において複雑な手順を踏む必要があり、その上、int
の最大値を上回る数の場合をカバーできていない。これらの切れ目が取り除かれるなることを期待している。)
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)