- 适用于 JDK 23 的 GraalVM(最新版本)
- 适用于 JDK 24 的 GraalVM(抢先体验版)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发版
- Truffle 语言实现框架
- Truffle 分支仪表
- 动态对象模型
- 静态对象模型
- 解释器代码的宿主优化
- Truffle 函数内联方法
- Truffle 解释器分析
- Truffle 交互 2.0
- 语言实现
- 使用 Truffle 实现新语言
- Truffle 语言和工具迁移到 Java 模块
- Truffle 本机函数接口
- 优化 Truffle 解释器
- 选项
- 栈上替换
- Truffle 字符串指南
- 特化直方图
- 测试 DSL 特化
- 基于多语言 API 的 TCK
- Truffle 编译队列方法
- Truffle 库指南
- Truffle AOT 概述
- Truffle AOT 编译
- 辅助引擎缓存
- Truffle 语言安全点教程
- 单态化
- 拆分算法
- 单态化用例
- 向运行时报告多态特化
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
)有两种变体:基于代码点的索引和基于字节的索引。基于字节的索引由操作名称中的 ByteIndex
后缀或前缀指示,否则索引基于代码点。例如,CodePointAtIndex
的索引参数是基于代码点的,而 CodePointAtByteIndex
使用基于字节的索引。
目前可用的操作列表如下,并按类别分组。
创建新的 TruffleString
- FromCodePoint: 从给定的代码点创建一个新的 TruffleString。
- FromLong: 从给定的长整型值创建一个新的 TruffleString。
- FromByteArray: 从给定的字节数组创建一个新的 TruffleString。
- FromCharArrayUTF16: 从给定的字符数组创建一个 UTF-16 TruffleString。
- FromIntArrayUTF32: 从给定的整数数组创建一个 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: 检查两个字符串是否相等。请注意,此操作对编码敏感!
- RegionEqual: 检查两个字符串在由基于代码点的偏移量和长度定义的给定区域中是否相等。
- RegionEqualByteIndex: 检查两个字符串在由基于字节的偏移量和长度定义的给定区域中是否相等。
- CompareBytes: 字节比较两个字符串。
- CompareCharsUTF16: 字符比较两个 UTF-16 字符串。
- CompareIntsUTF32: 整数比较两个 UTF-32 字符串。
- HashCode: 获取字符串的哈希码。哈希码基于字符串的字节,因此具有相同代码点但编码不同的字符串可能具有不同的哈希码。
转换
- SwitchEncoding: 将字符串转换为给定编码。
- ForceEncoding: 创建一个字符串,该字符串包含与给定字符串相同的字节,但分配给给定的编码。
- AsTruffleString: 将 MutableTruffleString 转换为不可变的 TruffleString。
- AsManaged: 将由本机指针支持的 TruffleString 转换为由 Java 字节数组支持的 TruffleString。
- ToValidString: 将 TruffleString 转换为编码正确的版本。
- CopyToByteArray: 将字符串的内容复制到字节数组中。
- GetInternalByteArray: 获取字符串的内部字节数组。
- CopyToNativeMemory: 将字符串的内容复制到本机指针中。
- GetInternalNativePointer: 获取本机字符串的指针对象。
- ToJavaString: 将字符串转换为
java.lang.String
。 - ParseInt: 将字符串的内容解析为整数。
- ParseLong: 将字符串的内容解析为长整型。
- ParseDouble: 将字符串的内容解析为双精度浮点型。
访问代码点和字节
- Materialize: 使用此节点避免在循环迭代字符串的代码点或字节时进行物化代码。
- ReadByte: 从字符串中读取单个字节。
- ReadCharUTF16: 从 UTF-16 字符串中读取单个字符。
- 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: 连接两个字符串。
- Substring: 从给定字符串中创建一个子字符串,由基于码点的偏移量和长度限定。
- SubstringByteIndex: 从给定字符串中创建一个子字符串,由基于字节的偏移量和长度限定。
- Repeat: 将给定字符串重复n次。
实例化 #
可以从码点、数字、基本类型数组或 java.lang.String
创建 TruffleString
。
可以使用 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
。
也可以通过 TruffleStringBuilder
创建 TruffleString
,它是 TruffleString
等效于 java.lang.StringBuilder
的类。
TruffleStringBuilder
提供以下操作
- AppendByte: 将单个字节追加到字符串生成器。
- AppendCharUTF16: 将单个字符追加到 UTF-16 字符串生成器。
- AppendCodePoint: 将单个码点追加到字符串生成器。
- AppendIntNumber: 将整数追加到字符串生成器。
- AppendLongNumber: 将长整数追加到字符串生成器。
- 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 语言注册 中设置 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
运行您的单元测试。这会在切换编码时禁用字符串对象的重用,并使编码检查更严格:在单个字符串上进行的所有操作都将强制执行精确匹配,而对两个字符串进行的操作仍然允许字节等效的重新解释。
所有具有两个以上字符串参数的 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
:此字符串中的所有码点都是基本拉丁 Unicode 块的一部分,也称为 ASCII(0x00 - 0x7f)。LATIN-1
:此字符串中的所有码点都是 ISO-8859-1 字符集的一部分(0x00 - 0xff),它等效于基本拉丁和拉丁-1 补充 Unicode 块的并集。字符串中的至少一个码点大于 0x7f。仅适用于 ISO-8859-1、UTF-16 和 UTF-32。BMP
:此字符串中的所有码点都是 Unicode 基本多语言平面 (BMP) 的一部分(0x0000 - 0xffff)。字符串中的至少一个码点大于 0xff。仅适用于 UTF-16 和 UTF-32。VALID
:此字符串已正确编码,并且包含至少一个不在其他适用代码范围内(例如,对于 UTF-8,这意味着有一个码点不在 ASCII 范围内,而对于 UTF-16,这意味着有一个码点不在 BMP 范围内)的码点。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));
}
}
字符串相等性和比较 #
应使用 EqualNode
检查 TruffleString
对象是否相等。与 HashCodeNode
一样,相等性比较对字符串的编码很敏感,因此在任何比较之前,字符串都必须始终转换为通用编码。Object#equals(Object)
的行为类似于 EqualNode
,但由于此方法没有 expectedEncoding
参数,它将自动确定字符串的通用编码。如果字符串的编码不相等,TruffleString
将检查一个字符串是否与另一个字符串的编码二进制兼容,如果是,则匹配它们的内容。否则,字符串被认为不相等,不会应用自动转换。
请注意,由于 TruffleString
的 hashCode
和 equals
方法对字符串编码敏感,因此在将 TruffleString
对象用作 HashMap
中的键之前,必须始终将它们转换为通用编码。
TruffleString
还提供了三个比较节点 CompareBytesNode
、CompareCharsUTF16Node
和 CompareIntsUTF32Node
,分别按字节、按字符和按整数比较字符串。
连接 #
连接通过 ConcatNode
完成。此操作要求两个字符串都使用 expectedEncoding
,这也是结果字符串的编码。延迟连接 通过 lazy
参数支持。当两个字符串被延迟连接时,新字符串的内部数组的分配和初始化将被延迟,直到另一个操作需要直接访问该数组。可以使用 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
,这意味着不会为结果字符串创建新的数组,而是重用父字符串的数组,并仅使用传递给子字符串节点的偏移量和长度进行访问。目前,延迟子字符串的内部数组永远不会被截断(即被替换为具有字符串精确长度的新数组)。请注意,此行为实际上会在创建延迟子字符串时创建内存泄漏。这可能会造成问题的极端示例:给定一个大小为 100 兆字节的字符串,从此字符串创建的任何延迟子字符串都将保持 100 兆字节数组的活动状态,即使原始字符串被垃圾收集器释放。谨慎使用延迟子字符串。
与 java.lang.String
的互操作性 #
TruffleString 提供 FromJavaStringNode
用于将 java.lang.String
转换为 TruffleString
。要从 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 字节,每个指针 4 字节,使用压缩 oops,8 字节内存对齐)。一个java.lang.String
对象包含一个指针字段、一个int
字段和一个byte
字段,在相同条件下,总对象大小为 24 字节。这种内存占用量的差异可能会对某些情况下生成大量小字符串的情况产生负面影响。TruffleString
就像java.lang.String
一样执行字符串压缩。- 如果您的语言需要将字符串转换为其他编码,例如 UTF-8(在 Web 应用程序中非常常见),
TruffleString
可以将此操作变为无操作,如果字符串不包含特殊字符。例如,仅包含 ASCII 的字符串可以重新解释为几乎任何编码,将仅包含 ASCII 的 UTF-16 字符串转换为 UTF-8 是无操作。在必须对字符串进行转码的情况下,TruffleString
将在原始字符串中缓存转码后的字符串,因此每个字符串和编码仅进行一次转码。 - 为了使用第三方库,
TruffleString
对象必须转换为java.lang.String
然后再转换回来。为了尽可能地降低成本,TruffleString
在从java.lang.String
转换为TruffleString
时重新使用 Java String 的内部字节数组,并在对象本身中缓存从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
,然后才能跨越语言边界。