Truffle文字列ガイド
Truffle文字列はTruffleのプリミティブ文字列型で、言語間で共有できます。言語の実装者は、相互運用性とパフォーマンスの向上のために、Truffle文字列を言語の文字列型として使用することをお薦めします。
TruffleString
は多数の文字列エンコーディングをサポートしますが、最も一般的に使用される次のものに対して特に最適化されています:
UTF-8
UTF-16
UTF-32
US-ASCII
ISO-8859-1
BYTES
TruffleString API
TruffleString
によって公開されるすべての操作は、内部Node
および静的メソッドまたはインスタンス・メソッドとして提供されています。可能な場合は、指定されたノードを使用する必要があります。静的/インスタンス・メソッドは、それぞれのノードのキャッシュされていないバージョンを実行するための簡易な方法であるためです。すべてのノードの名前は{NameOfOperation}Node
で、すべての簡易メソッドの名前は{nameOfOperation}Uncached
です。
一部の操作では、特定の文字列プロパティの遅延連結や遅延評価など、遅延評価がサポートされています。これらの操作のほとんどはパラメータboolean lazy
を提供しており、これによりユーザーはコールサイトごとに遅延評価を有効または無効にできます。
CodePointAtIndex
などの索引値を扱う操作は、コードポイントベースの索引付けとバイトベースの索引付けの2つのバリアントで使用できます。バイトベースの索引付けは、操作名にByteIndex
接尾辞または接頭辞を付けて示されます。それ以外の場合、索引はコードポイントベースです。たとえば、CodePointAtIndex
の索引パラメータはコードポイントベースですが、CodePointAtByteIndex
はバイトベースの索引を使用します。
現在使用可能な操作のリストを次に示し、カテゴリ別にグループ化しています。
新しいTruffleString
の作成:
- FromCodePoint: 特定のコードポイントから新しいTruffleStringを作成します。
- FromLong: 指定されたlong値から新しいTruffleStringを作成します。
- FromByteArray: 指定されたバイト配列から新しいTruffleStringを作成します。
- FromCharArrayUTF16: 特定のchar配列からUTF-16 TruffleStringを作成します。
- FromIntArrayUTF32: 特定のint配列からUTF-32 TruffleStringを作成します。
- FromJavaString: 特定の
java.lang.String
からTruffleStringを作成します。 - FromNativePointer: 特定のネイティブ・ポインタから新しいTruffleStringを作成します。
- Encoding#getEmpty: そのエンコーディングで空のTruffleStringを取得します。
問合せ文字列プロパティ:
- isEmpty: 文字列が空かどうかを確認します。
- CodePointLength: コードポイントで文字列の長さを取得します。
- byteLength: 文字列の長さをバイト単位で取得します。
- IsValid: 文字列が正しくエンコードされているかどうかを確認します。
- GetCodeRange: 文字列の内容に関する大まかな情報を取得します(ASCII/LATIN-1/BMPの範囲のこの文字列のすべてのコードポイント)。
- GetByteCodeRange: 16/32ビット・ベースのエンコーディングを考慮せずに、文字列の内容に関する大まかな情報を取得します。
- CodeRangeEquals: 文字列のコード範囲が指定されたコード範囲と等しいかどうかを確認します。
- isCompatibleTo: 文字列が指定したエンコーディングと互換性があるかどうか、これを表示できるかどうかを確認します。
- isManaged: 文字列がネイティブ・ポインタをベースにしていないことを確認します。
- isNative: 文字列がネイティブ・ポインタによってサポートされているかどうかを確認します。
- isImmutable: 文字列が
TruffleString
のインスタンスであるかどうかを確認します。 - isMutable: 文字列が
MutableTruffleString
のインスタンスであるかどうかを確認します。
比較:
- Equal: 2つの文字列が等しいかどうかを確認します。この操作はエンコーディングに依存します。
- RegionEqual: コードポイントベースのオフセットおよび長さによって定義された特定のリージョンで2つの文字列が等しいかどうかを確認します。
- RegionEqualByteIndex: バイトベースのオフセットおよび長さによって定義された特定のリージョンで2つの文字列が等しいかどうかを確認します。
- CompareBytes: 2つの文字列をバイト単位で比較します。
- CompareCharsUTF16: 2つのUTF-16文字列を文字単位で比較します。
- CompareIntsUTF32: 2つのUTF-32文字列を整数単位で比較します。
- HashCode: 文字列のハッシュ・コードを取得します。ハッシュ・コードは文字列のバイトに基づいているため、コードポイントが同じでもエンコーディングが異なる文字列は異なるハッシュ・コードを持つ場合があります。
変換:
- SwitchEncoding: 文字列を特定のエンコーディングに変換します。
- ForceEncoding: 特定の文字列と同じバイトを含む文字列を作成しますが、特定のエンコーディングに割り当てられます。
- AsTruffleString: MutableTruffleStringを不変のTruffleStringに変換します。
- AsManaged: ネイティブ・ポインタをベースにしたTruffleStringを、javaバイト配列をベースにしたものに変換します。
- CopyToByteArray: 文字列の内容をバイト配列にコピーします。
- GetInternalByteArray: 文字列の内部バイト配列を取得します。
- CopyToNativeMemory: 文字列の内容をネイティブ・ポインタにコピーします。
- GetInternalNativePointer: ネイティブ文字列のポインタ・オブジェクトを取得します。
- ToJavaString: 文字列を
java.lang.String
に変換します。 - ParseInt: 文字列の内容をint値として解析します。
- ParseLong: 文字列の内容をlong値として解析します。
- ParseDouble: 文字列の内容をdouble値として解析します。
コードポイントおよびバイトへのアクセス:
- Materialize: このノードは、文字列のコード・ポイントまたはバイトを反復するループ内のマテリアライズド・コードを回避するために使用します。
- ReadByte: 文字列から1バイトを読み取ります。
- ReadCharUTF16: UTF-16文字列から1つの文字を読み取ります。
- CodePointAtIndex: 特定のコードポイントベースの索引の文字列から単一のコードポイントを読み取ります。
- CodePointAtByteIndex: 指定されたバイトベースの索引の文字列から単一のコードポイントを読み取ります。
- CreateCodePointIterator: 文字列のコードポイントの反復に適した
TruffleStringIterator
オブジェクトを返します。 - CreateBackwardCodePointIterator: 文字列のコードポイントの反復に適した
TruffleStringIterator
オブジェクトを、文字列の最後から返します。 - ByteLengthOfCodePoint: 特定のバイト索引で始まるコードポイントによって占有されているバイト数を返します。
- CodePointIndexToByteIndex: 特定のコードポイント索引を特定の文字列のバイト索引に変換します。
- ByteIndexToCodePointIndex: 指定されたバイト索引を指定された文字列のコードポイント索引に変換します。
検索:
- ByteIndexOfAnyByte: 文字列内の指定されたバイト・セットの最初の出現を検索し、そのバイトベースの索引を返します。
- CharIndexOfAnyCharUTF16: UTF-16文字列で特定の文字セットの最初の出現を検出し、その文字ベースの索引を返します。
- IntIndexOfAnyIntUTF32: UTF-32文字列の指定された整数セットの最初の出現を検索し、その整数ベースの索引を返します。
- IndexOfCodePoint: 文字列内で指定されたコードポイントの最初の出現を検出し、そのコードポイントベースの索引を返します。
- ByteIndexOfCodePoint: 文字列内で指定されたコードポイントの最初の出現を検出し、そのバイトベースの索引を返します。
- ByteIndexOfCodePointSet: 文字列内の指定されたセットに含まれるコードポイントの最初の出現を検索し、そのバイトベースの索引を返します。
- LastIndexOfCodePoint: 文字列内で指定されたコードポイントの最後の出現を検出し、そのコードポイントベースの索引を返します。
- LastByteIndexOfCodePoint: 文字列内で指定されたコードポイントの最後の出現を検出し、そのバイトベースの索引を返します。
- IndexOfString: 文字列内の指定された部分文字列の最初の出現を検索し、そのコードポイントベースの索引を返します。
- ByteIndexOfString: 文字列内の指定された部分文字列の最初の出現を検索し、そのバイトベースの索引を返します。
- LastIndexOfString: 文字列内の指定された部分文字列の最後の出現を検索し、そのコードポイントベースの索引を返します。
- LastByteIndexOfString: 文字列内の指定された部分文字列の最後の出現を検索し、そのバイトベースの索引を返します。
組合せ:
- Concat: 2つの文字列を連結します。
- 部分文字列: コードポイントベースのオフセットと長さでバインドされた、指定された文字列から部分文字列を作成します。
- SubstringByteIndex: バイトベースのオフセットと長さでバインドされた、指定された文字列から部分文字列を作成します。
- Repeat: 指定された文字列を n回繰り返します。
インスタンス化
TruffleString
は、コードポイント、数値、プリミティブ配列またはjava.lang.String
から作成できます。
任意のエンコーディングの文字列はTruffleString.FromByteArrayNode
で作成でき、すでにエンコードされた文字列を含むバイト配列が想定されます。copy
パラメータをfalse
に設定することで、この操作をコピーできないようにします。
重要: TruffleStrings
は、配列の内容が不変であると想定し、この操作のコピーできないバリアントに配列を渡した後にこれを変更しません。
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.strings.TruffleString;
abstract static class SomeNode extends Node {
@Specialization
static TruffleString someSpecialization(
@Cached TruffleString.FromByteArrayNode fromByteArrayNode) {
byte[] array = {'a', 'b', 'c'};
return fromByteArrayNode.execute(array, 0, array.length, TruffleString.Encoding.UTF_8, false);
}
}
システムのエンディアンネスに関係なく、UTF-16およびUTF-32文字列を簡単に作成できるように、TruffleString
はTruffleString.FromCharArrayUTF16Node
およびTruffleString.FromIntArrayUTF32Node
を提供します。
TruffleString
は、TruffleStringBuilder
を介して作成することもできます。TruffleString
は、java.lang.StringBuilder
と同等です。
TruffleStringBuilder
は、次の操作を提供します:
- AppendByte: 文字列ビルダーに1バイトを追加します。
- AppendCharUTF16: UTF-16文字列ビルダーに1文字を追加します。
- AppendCodePoint: 文字列ビルダーに単一のコードポイントを追加します。
- AppendIntNumber: 文字列ビルダーに整数を追加します。
- AppendLongNumber: 文字列ビルダーにlong数値を追加します。
- AppendString: 文字列ビルダーにTruffleStringを追加します。
- AppendSubstringByteIndex: バイトベースのオフセットと長さで定義された部分文字列を文字列ビルダーに追加します。
- AppendJavaStringUTF16: 文字ベースのオフセットおよび長さで定義されたJava文字列の部分文字列を文字列ビルダーに追加します。
- ToString: 文字列ビルダーから新しいTruffleStringを作成します。
次の例を参照してください:
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.api.strings.TruffleStringBuilder;
abstract static class SomeNode extends Node {
@Specialization
static TruffleString someSpecialization(
@Cached TruffleStringBuilder.AppendCharUTF16Node appendCharNode,
@Cached TruffleStringBuilder.AppendJavaStringUTF16Node appendJavaStringNode,
@Cached TruffleStringBuilder.AppendIntNumberNode appendIntNumberNode,
@Cached TruffleStringBuilder.AppendStringNode appendStringNode,
@Cached TruffleString.FromCharArrayUTF16Node fromCharArrayUTF16Node,
@Cached TruffleStringBuilder.AppendCodePointNode appendCodePointNode,
@Cached TruffleStringBuilder.ToStringNode toStringNode) {
TruffleStringBuilder sb = TruffleStringBuilder.create(TruffleString.Encoding.UTF_16);
sb = appendCharNode.execute(sb, 'a');
sb = appendJavaStringNode.execute(sb, "abc", /* fromIndex: */ 1, /* length: */ 2);
sb = appendIntNumberNode.execute(sb, 123);
TruffleString string = fromCharArrayUTF16Node.execute(new char[]{'x', 'y'}, /* fromIndex: */ 0, /* length: */ 2);
sb = appendStringNode.execute(sb, string);
sb = appendCodePointNode.execute(sb, 'z');
return toStringNode.execute(sb); // string content: "abc123xyz"
}
}
エンコーディング
すべてのTruffleString
は、インスタンス化時に設定される特定の内部エンコーディングでエンコードされます。
TruffleString
は、次のエンコーディング用に完全に最適化されています:
UTF-8
UTF-16
UTF-32
US-ASCII
ISO-8859-1
BYTES
その他の多くのエンコーディングがサポートされていますが、完全に最適化されているわけではありません。これらを使用するには、Truffle language registrationでneedsAllEncodings = true
を設定して有効にする必要があります。
TruffleString
の内部エンコーディングは公開されません。文字列のエンコーディングを問い合せるかわりに、言語は、文字列のエンコーディングが重要なすべてのメソッド(ほとんどすべての操作)にexpectedEncoding
パラメータを渡す必要があります。これにより、文字列が両方のエンコーディングでバイト等価である場合に、エンコーディング間の変換時に文字列オブジェクトを再利用できます。次の例に示すように、文字列はSwitchEncodingNode
を使用して別のエンコーディングに変換できます:
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.api.strings.TruffleStringBuilder;
abstract static class SomeNode extends Node {
@Specialization
static void someSpecialization(
@Cached TruffleString.FromJavaStringNode fromJavaStringNode,
@Cached TruffleString.ReadByteNode readByteNode,
@Cached TruffleString.SwitchEncodingNode switchEncodingNode,
@Cached TruffleString.ReadByteNode utf8ReadByteNode) {
// instantiate a new UTF-16 string
TruffleString utf16String = fromJavaStringNode.execute("foo", TruffleString.Encoding.UTF_16);
// read a byte with expectedEncoding = UTF-16.
// if the string is not byte-compatible with UTF-16, this method will throw an IllegalArgumentException
System.out.printf("%x%n", readByteNode.execute(utf16String, /* byteIndex */ 0, TruffleString.Encoding.UTF_16));
// convert to UTF-8.
// note that utf8String may be reference-equal to utf16String!
TruffleString utf8String = switchEncodingNode.execute(utf16String, TruffleString.Encoding.UTF_8);
// read a byte with expectedEncoding = UTF-8
// if the string is not byte-compatible with UTF-8, this method will throw an IllegalArgumentException
System.out.printf("%x%n", utf8ReadByteNode.execute(utf8String, /* byteIndex */ 0, TruffleString.Encoding.UTF_8));
}
}
エンコーディング間のバイト等価性は、UTF-16およびUTF-32で文字列圧縮を使用して決定されます。たとえば、圧縮されたUTF-16文字列はISO-8859-1とバイト等価で、すべての文字がASCII範囲(CodeRange
を参照)にある場合は、UTF-8とバイト等価です。
コードが正しくエンコーディングを切り替えているかどうかを確認するには、システム・プロパティtruffle.strings.debug-strict-encoding-checks=true
を使用してユニット・テストを実行します。これにより、エンコーディングを切り替えるときに文字列オブジェクトを再利用できなくなり、エンコーディング・チェックがより厳密になります。1つの文字列で動作するすべての操作では完全一致が強制されますが、2つの文字列で動作する操作ではバイト等価の再解釈が可能になります。
複数の文字列パラメータを持つすべてのTruffleString
操作では、文字列が結果エンコーディングと互換性のあるエンコーディングである必要があります。したがって、文字列は同じエンコーディングである必要があります。また、コール元は両方の文字列が結果のエンコーディングと互換性があることを確認する必要があります。これにより、SwitchEncodingNodes
が何も行わないことをすでに認識しているコール元は、フットプリントの理由からこれらをスキップできるようになります。
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.api.strings.TruffleStringBuilder;
abstract static class SomeNode extends Node {
@Specialization
static boolean someSpecialization(
TruffleString a,
TruffleString b,
@Cached TruffleString.SwitchEncodingNode switchEncodingNodeA,
@Cached TruffleString.SwitchEncodingNode switchEncodingNodeB,
@Cached TruffleString.EqualNode equalNode) {
TruffleString utf8A = switchEncodingNodeA.execute(a, TruffleString.Encoding.UTF_8);
TruffleString utf8B = switchEncodingNodeB.execute(b, TruffleString.Encoding.UTF_8);
return equalNode.execute(utf8A, utf8B, TruffleString.Encoding.UTF_8);
}
}
文字列のプロパティ
TruffleString
では、次のプロパティが公開されます:
byteLength
:byteLength
メソッドを介して公開される文字列の長さ(バイト単位)。codePointLength
:CodePointLengthNode
を介して公開されるコードポイント内の文字列の長さ。isValid
:IsValidNode
で問い合せて、文字列が正しくエンコードされているかどうかを確認できます。codeRange
:GetCodeRangeNode
を介して公開される文字列の内容に関する大まかな情報を提供します。このプロパティには、次の値を指定できます。ASCII
: この文字列のすべてのコードポイントは、Basic Latin Unicodeブロックの一部で、ASCII (0x00 - 0x7f)とも呼ばれます。LATIN-1
: この文字列のすべてのコードポイントは、ISO-8859-1文字セット(0x00 - 0xff)の一部で、Basic LatinとLatin-1 Supplement Unicodeブロックを組み合せたものと等価です。文字列内の少なくとも1つのコードポイントが0x7fを超えています。ISO-8859-1、UTF-16およびUTF-32にのみ適用されます。BMP
: この文字列のすべてのコードポイントは、Unicode Basic Multilingual Plane (BMP) (0x0000 - 0xffff)の一部です。文字列内の少なくとも1つのコードポイントが0xffを超えています。UTF-16およびUTF-32にのみ適用されます。VALID
: この文字列は正しくエンコードされており、他の適用可能なコード範囲外のコードポイントが少なくとも1つ含まれています(UTF-8の場合、これはASCII範囲外のコードポイントが1つあることを意味します。UTF-16の場合、BMP範囲外のコードポイントが1つあることを意味します)。BROKEN
: この文字列は正しくエンコードされていません。その内容に関する情報はこれ以上決定できません。
hashCode
:HashCodeNode
を介して公開される文字列のハッシュ・コード。ハッシュ・コードは文字列のエンコーディングに依存します。ハッシュ・コードを比較する前に、文字列は常に共通エンコーディングに変換する必要があります。
TruffleString
で公開されているすべてのプロパティを問い合せるには、次の例を参照してください:
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.strings.TruffleString;
abstract static class SomeNode extends Node {
@Specialization
static TruffleString someSpecialization(
TruffleString string,
@Cached TruffleString.CodePointLengthNode codePointLengthNode,
@Cached TruffleString.IsValidNode isValidNode,
@Cached TruffleString.GetCodeRangeNode getCodeRangeNode,
@Cached TruffleString.HashCodeNode hashCodeNode) {
System.out.println("byte length: " + string.byteLength(TruffleString.Encoding.UTF_8));
System.out.println("codepoint length: " + codePointLengthNode.execute(string, TruffleString.Encoding.UTF_8));
System.out.println("is valid: " + isValidNode.execute(string));
System.out.println("code range: " + getCodeRangeNode.execute(string));
System.out.println("hash code: " + hashCodeNode.execute(string, TruffleString.Encoding.UTF_8));
}
}
文字列の等式と比較式
TruffleString
オブジェクトは、EqualNode
を使用して等価性をチェックする必要があります。HashCodeNode
と同様に、等価比較は文字列のエンコーディングに依存するため、比較の前に文字列は常に共通エンコーディングに変換する必要があります。Object#equals(Object)
はEqualNode
と似ていますが、このメソッドにはexpectedEncoding
パラメータがないため、文字列の共通エンコーディングが自動的に決定されます。文字列のエンコーディングが等しくない場合、TruffleString
は、文字列がもう一方の文字列のエンコーディングとバイナリ互換性があるかどうかを確認し、一致する場合はその内容と照合します。それ以外の場合、文字列は等しくないとみなされ、自動変換は適用されません。
TruffleString
のhashCode
およびequals
メソッドは文字列エンコーディングに依存するため、TruffleString
オブジェクトは、HashMap
でキーとして使用する前に、常に共通エンコーディングに変換される必要があります。
TruffleString
には、3つの比較ノードCompareBytesNode
、CompareCharsUTF16Node
およびCompareIntsUTF32Node
も用意されており、それぞれバイト単位、文字単位および整数単位で文字列を比較します。
連結
連結はConcatNode
を介して行われます。この操作では、両方の文字列がexpectedEncoding
に存在する必要があります。これは、結果の文字列のエンコーディングでもあります。遅延連結は、lazy
パラメータを介してサポートされます。2つの文字列が遅延して連結される場合、新しい文字列の内部配列の割当ておよび初期化は、別の操作がその配列への直接アクセスを必要とするまで遅延されます。このような「遅延連結文字列」のマテリアライズは、MaterializeNode
で明示的にトリガーできます。これは、次の例のように、ループ内の文字列にアクセスする前に便利です:
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.strings.TruffleString;
abstract static class SomeNode extends Node {
@Specialization
static TruffleString someSpecialization(
TruffleString utf8StringA,
TruffleString utf8StringB,
@Cached TruffleString.ConcatNode concatNode,
@Cached TruffleString.MaterializeNode materializeNode,
@Cached TruffleString.ReadByteNode readByteNode) {
// lazy concatenation
TruffleString lazyConcatenated = concatNode.execute(utf8StringA, utf8StringB, TruffleString.Encoding.UTF_8, /* lazy */ true);
// explicit materialization
TruffleString materialized = materializeNode.execute(lazyConcatenated, TruffleString.Encoding.UTF_8);
int byteLength = materialized.byteLength(TruffleString.Encoding.UTF_8);
for (int i = 0; i < byteLength; i++) {
// string is guaranteed to be materialized here, so no slow materialization code can end up in this loop
System.out.printf("%x%n", readByteNode.execute(materialized, i, TruffleString.Encoding.UTF_8));
}
}
}
部分文字列
部分文字列はSubstringNode
およびSubstringByteIndexNode
を介して作成でき、それぞれコードポイントベースおよびバイトベースの索引を使用します。部分文字列もlazy
にできます。つまり、結果の文字列に対して新しい配列は作成されませんが、かわりに親文字列の配列が再使用され、部分文字列ノードに渡されるオフセットと長さでアクセスされます。現在、遅延部分文字列の内部配列は切り捨てられません(文字列の正確な長さの新しい配列に置き換えられます)。この動作では、遅延部分文字列が作成されるたびにメモリー・リークが効率的に作成されます。これが問題となる可能性のある極端な例: サイズが100MBの文字列を指定すると、この文字列から作成された遅延部分文字列は、元の文字列がガベージ・コレクタによって解放された場合でも、100MBの配列を保持します。遅延部分文字列は慎重に使用してください。
java.lang.String
との相互運用性
TruffleStringは、java.lang.String
をTruffleString
に変換するためのFromJavaStringNode
を提供します。TruffleString
からjava.lang.String
に変換するには、ToJavaStringNode
を使用します。このノードは、必要に応じて文字列をUTF-16に内部的に変換し、その表現からjava.lang.String
を作成します。
Object#toString()
は、キャッシュされていないバージョンのToJavaStringNode
を使用して実装されるため、高速パスでは回避する必要があります。
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.strings.TruffleString;
abstract static class SomeNode extends Node {
@Specialization
static void someSpecialization(
@Cached TruffleString.FromJavaStringNode fromJavaStringNode,
@Cached TruffleString.SwitchEncodingNode switchEncodingNode,
@Cached TruffleString.ToJavaStringNode toJavaStringNode,
@Cached TruffleString.ReadByteNode readByteNode) {
TruffleString utf16String = fromJavaStringNode.execute("foo", TruffleString.Encoding.UTF_16);
TruffleString utf8String = switchEncodingNode.execute(utf16String, TruffleString.Encoding.UTF_8);
System.out.println(toJavaStringNode.execute(utf8String));
}
}
TruffleString
は、デバッグのために#toStringDebug()
も公開します。戻り値は指定されず、いつでも変更される可能性があるため、デバッグ以外にはこのメソッドを使用しないでください。
java.lang.String
との違い
java.lang.String
からTruffleString
に切り替えるときは、次の項目を考慮する必要があります:
TruffleString
インスタンスの静的オーバーヘッドは、java.lang.String
オブジェクトのインスタンスよりも大きくなります。TruffleString
オブジェクトには、2つのポインタ・フィールド、4つのint
フィールドおよび4つのbyte
フィールドが含まれており、通常は合計オブジェクト・サイズが40バイトになります(12バイトのオブジェクト・ヘッダー、圧縮oopsを含むポインタごとに4バイト、8バイトのメモリー配置)。java.lang.String
オブジェクトには、1つのポインタ・フィールド、1つのint
フィールドと1つのbyte
フィールドが含まれており、同じ条件では合計オブジェクト・サイズが24バイトになります。このメモリー・フットプリントの違いは、多数の小さな文字列が生成されるケースに悪影響を与える可能性があります。TruffleString
は、java.lang.String
と同様に文字列圧縮を実行します。- 言語で文字列を他のエンコーディング(Webアプリケーションで非常に一般的なUTF-8など)に変換する必要がある場合、文字列に特殊文字が含まれると、
TruffleString
はこの操作をno-opに変換できます。たとえば、ASCIIのみの文字列を任意のエンコーディングとして再解釈でき、ASCIIのみのUTF-16文字列をUTF-8に変換することはありません。文字列のトランスコーディングを回避できない場合、TruffleString
はトランスコードされた文字列を元の文字列にキャッシュするため、トランスコーディングは文字列とエンコーディングごとに1回のみ行われます。 - サード・パーティ・ライブラリを使用するには、
TruffleString
オブジェクトをjava.lang.String
に変換して戻す必要があります。これのコストを可能なかぎり低くするために、TruffleString
はjava.lang.String
からTruffleString
に変換するときにJava文字列の内部バイト配列を再利用し、オブジェクト自体でTruffleString
オブジェクトから作成されたJava文字列をキャッシュします。 TruffleString
は、java.lang.String
に存在しない追加機能を提供します:- 遅延連結ビューおよび文字列ビュー。言語で実行する必要のある配列コピー操作の量を大幅に減少できます。
- ネイティブ・メモリーへの
String
ビュー。使用する前にネイティブ・メモリーをJava配列にコピーする必要が完全に回避されます。 codeRange
プロパティを介したString
の内容の分類。ASCIIのみの文字列に対する特殊化を許可します。これにより、一部の文字列操作の複雑性が大幅に低下する可能性があります。
- すべての
TruffleString
操作のパフォーマンスは、対応するjava.lang.String
と同等かそれ以上である必要があります。
コードポイント・イテレータ
TruffleString
は、文字列のコードポイントを反復する手段としてTruffleStringIterator
を提供します。CodePointAtIndexNode
は、特定のコードポイント索引と同等のバイト索引をすべてのコールで再計算する必要がある可能性があるため、特にUTF-8などの可変長幅エンコーディングに対して、ループでCodePointAtIndexNode
を使用する方法よりも、このメソッドを優先する必要があります。
例を参照してください:
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.api.strings.TruffleStringIterator;
abstract static class SomeNode extends Node {
@Specialization
static void someSpecialization(
TruffleString string,
@Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode,
@Cached TruffleStringIterator.NextNode nextNode,
@Cached TruffleString.CodePointLengthNode codePointLengthNode,
@Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode) {
// iterating over a string's codepoints using TruffleStringIterator
TruffleStringIterator iterator = createCodePointIteratorNode.execute(string, TruffleString.Encoding.UTF_8);
while (iterator.hasNext()) {
System.out.printf("%x%n", nextNode.execute(iterator));
}
// suboptimal variant: using CodePointAtIndexNode in a loop
int codePointLength = codePointLengthNode.execute(string, TruffleString.Encoding.UTF_8);
for (int i = 0; i < codePointLength; i++) {
// performance problem: codePointAtIndexNode may have to calculate the byte index corresponding
// to codepoint index i for every loop iteration
System.out.printf("%x%n", codePointAtIndexNode.execute(string, i, TruffleString.Encoding.UTF_8));
}
}
}
可変文字列
TruffleString
は、MutableTruffleString
と呼ばれる可変文字列バリアントも提供します。これは、TruffleString
のすべてのノードでも受け入れられます。MutableTruffleString
はスレッドセーフではないため、WriteByteNode
を介して、内部バイト配列またはネイティブ・ポインタのバイトを上書きできます。内部配列またはネイティブ・ポインタの内容は外部で変更することもできますが、対応するMutableTruffleString
にはnotifyExternalMutation()
を介して、これを通知する必要があります。MutableTruffleString
は、Truffle相互運用性の型ではなく、言語境界を渡す前に、TruffleString.AsTruffleString
を介して不変のTruffleString
に変換する必要があります。