- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 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 Strings 是 Truffle 的原始字符串类型,可以在不同语言之间共享。建议语言实现者将 Truffle Strings 用作其语言的字符串类型,以实现更简单的互操作性和更好的性能。
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:从给定 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:检查两个字符串是否相等。请注意,此操作对编码敏感!
- RegionEqual:检查两个字符串在由码点偏移量和长度定义的给定区域内是否相等。
- RegionEqualByteIndex:检查两个字符串在由字节偏移量和长度定义的给定区域内是否相等。
- CompareBytes:逐字节比较两个字符串。
- CompareCharsUTF16:逐字符比较两个 UTF-16 字符串。
- CompareIntsUTF32:逐 int 比较两个 UTF-32 字符串。
- HashCode:获取字符串的哈希码。哈希码基于字符串的字节,因此具有相同码点但不同编码的字符串可能具有不同的哈希码。
转换
- SwitchEncoding:将字符串转换为给定编码。
- ForceEncoding:创建一个字符串,其包含的字节与给定字符串相同,但分配给给定编码。
- AsTruffleString:将 MutableTruffleString 转换为不可变 TruffleString。
- AsManaged:将由原生指针支持的 TruffleString 转换为由 Java 字节数组支持的 TruffleString。
- ToValidString:将 TruffleString 转换为正确编码的版本。
- CopyToByteArray:将字符串内容复制到字节数组中。
- GetInternalByteArray:获取字符串的内部字节数组。
- CopyToNativeMemory:将字符串内容复制到原生指针中。
- GetInternalNativePointer:获取原生字符串的指针对象。
- ToJavaString:将字符串转换为 java.lang.String。
- ParseInt:将字符串内容解析为 int 值。
- ParseLong:将字符串内容解析为 long 值。
- ParseDouble:将字符串内容解析为 double 值。
访问码点和字节
- Materialize:使用此节点可避免在遍历字符串码点或字节的循环中出现物化代码。
- ReadByte:从字符串中读取单个字节。
- ReadCharUTF16:从 UTF-16 字符串中读取单个字符。
- CodePointAtIndex:从字符串中读取给定码点索引处的单个码点。
- CodePointAtByteIndex:从字符串中读取给定字节索引处的单个码点。
- CreateCodePointIterator:返回适合迭代字符串码点的 TruffleStringIterator对象。
- CreateBackwardCodePointIterator:返回适合从字符串末尾开始迭代字符串码点的 TruffleStringIterator对象。
- ByteLengthOfCodePoint:返回从给定字节索引开始的码点所占用的字节数。
- CodePointIndexToByteIndex:将给定码点索引转换为给定字符串上的字节索引。
- ByteIndexToCodePointIndex:将给定字节索引转换为给定字符串上的码点索引。
搜索
- ByteIndexOfAnyByte:在字符串中查找给定字节集中的任意字节的第一次出现,并返回其基于字节的索引。
- CharIndexOfAnyCharUTF16:在 UTF-16 字符串中查找给定字符集中的任意字符的第一次出现,并返回其基于字符的索引。
- IntIndexOfAnyIntUTF32:在 UTF-32 字符串中查找给定 int 集中的任意 int 的第一次出现,并返回其基于 int 的索引。
- IndexOfCodePoint:在字符串中查找给定码点的第一次出现,并返回其基于码点的索引。
- ByteIndexOfCodePoint:在字符串中查找给定码点的第一次出现,并返回其基于字节的索引。
- ByteIndexOfCodePointSet:在字符串中查找包含在给定集中的码点的第一次出现,并返回其基于字节的索引。
- LastIndexOfCodePoint:在字符串中查找给定码点的最后一次出现,并返回其基于码点的索引。
- LastByteIndexOfCodePoint:在字符串中查找给定码点的最后一次出现,并返回其基于字节的索引。
- IndexOfString:在字符串中查找给定子字符串的第一次出现,并返回其基于码点的索引。
- ByteIndexOfString:在字符串中查找给定子字符串的第一次出现,并返回其基于字节的索引。
- LastIndexOfString:在字符串中查找给定子字符串的最后一次出现,并返回其基于码点的索引。
- LastByteIndexOfString:在字符串中查找给定子字符串的最后一次出现,并返回其基于字节的索引。
组合
- Concat:连接两个字符串。
- Substring:从给定字符串创建子字符串,由基于码点的偏移量和长度界定。
- 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:向字符串构建器追加单个字节。
- 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));
    }
}
字符串相等性和比较 #
TruffleString 对象应使用 EqualNode 检查相等性。就像 HashCodeNode 一样,相等性比较对字符串的编码敏感,因此在任何比较之前,字符串应始终转换为通用编码。Object#equals(Object) 的行为类似于 EqualNode,但由于此方法没有 expectedEncoding 参数,它将自动确定字符串的通用编码。如果字符串的编码不相等,TruffleString 将检查一个字符串是否与另一个字符串的编码二进制兼容,如果是,则匹配它们的内容。否则,字符串被视为不相等,不应用自动转换。
请注意,由于 TruffleString 的 hashCode 和 equals 方法对字符串编码敏感,因此在使用 TruffleString 对象作为 HashMap 中的键等操作之前,必须始终将其转换为通用编码。
TruffleString 还提供三个比较节点:CompareBytesNode、CompareCharsUTF16Node 和 CompareIntsUTF32Node,分别用于逐字节、逐字符和逐 int 比较字符串。
连接 #
连接通过 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 String 时将 Java Strings 缓存到对象本身中。
- 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 覆盖其内部字节数组或原生指针中的字节。内部数组或原生指针的内容也可以在外部修改,但必须通过 notifyExternalMutation() 通知相应的 MutableTruffleString。MutableTruffleString 不是 Truffle 互操作类型,在跨越语言边界之前必须通过 TruffleString.AsTruffleString 转换为不可变的 TruffleString。