TextEncoder: encodeInto() 方法

TextEncoder.encodeInto() 方法接受要编码的字符串和一个目标 Uint8Array 用于存放生成的 UTF-8 编码文本,并返回一个指示编码进度的字典对象。这可能比旧的 encode() 方法更有效率——尤其是在目标缓冲区是 Wasm 堆的视图时。

语法

js
encodeInto(string, uint8Array)

参数

string

包含要编码文本的字符串。

uint8Array

一个 Uint8Array 对象实例,用于存放生成的 UTF-8 编码文本。

返回值

一个对象,包含两个成员

read

已从源代码转换为 UTF-8 的 UTF-16 代码单元的数量。如果 uint8Array 空间不足,则此值可能小于 string.length

written

在目标 Uint8Array 中修改的字节数。保证写入的字节形成完整的 UTF-8 字节序列。

编码到特定位置

encodeInto() 始终将其输出放在数组的开头。但是,有时使输出从特定索引开始很有用。解决方案是 TypedArray.prototype.subarray()

js
const encoder = new TextEncoder();

function encodeIntoAtPosition(string, u8array, position) {
  return encoder.encodeInto(
    string,
    position ? u8array.subarray(position | 0) : u8array,
  );
}

const u8array = new Uint8Array(8);
encodeIntoAtPosition("hello", u8array, 2);
console.log(u8array.join()); // 0,0,104,101,108,108,111,0

缓冲区大小

要转换 JavaScript 字符串 s,完全转换所需的输出空间永远不少于 s.length 字节,且不多于 s.length * 3 字节。字符串的 UTF-8 到 UTF-16 长度比率取决于您使用的语言。

  • 对于主要使用 ASCII 字符的基本英文文本,比率接近 1。
  • 对于使用 U+0080 到 U+07FF 之间字符的脚本(包括希腊语、西里尔语、希伯来语、阿拉伯语等)的文本,比率约为 2。
  • 对于使用 U+0800 到 U+FFFF 之间字符的脚本(包括中文、日语、韩语等)的文本,比率约为 3。
  • 整个脚本不太可能完全使用 非 BMP 字符(尽管确实存在)。这些字符通常是数学符号、表情符号、历史脚本等。这些字符的比率为 2,因为它们在 UTF-8 中占用 4 个字节,在 UTF-16 中占用 2 个字节。

如果预期输出分配(通常在 Wasm 堆中)是短暂的,则为输出分配 s.length * 3 字节是有意义的,在这种情况下,保证第一次转换尝试将转换整个字符串。

例如,如果您的文本主要是英文,则长文本不太可能超过 s.length * 2 字节。因此,更乐观的方法可能是分配 s.length * 2 + 5 字节,并在乐观预测错误的罕见情况下执行重新分配。

如果预期输出是长期存在的,则计算最小分配 roundUpToBucketSize(s.length)、最大分配大小 s.length * 3,并选择一个阈值 t(作为内存使用量和速度之间的权衡),以便如果 roundUpToBucketSize(s.length) + t >= s.length * 3,则为 s.length * 3 分配内存。否则,首先为 roundUpToBucketSize(s.length) 分配内存并进行转换。如果返回值字典中的 read 项目为 s.length,则转换完成。否则,将目标缓冲区重新分配到 written + (s.length - read) * 3,然后通过获取从索引 read 开始的 s 的子字符串和从索引 written 开始的目标缓冲区的子缓冲区来转换其余部分。

上面的 roundUpToBucketSize() 是一个向上舍入到分配器桶大小的函数。例如,如果已知您的 Wasm 分配器使用 2 的幂桶,则如果 roundUpToBucketSize() 的参数是 2 的幂,则应返回该参数,否则应返回下一个 2 的幂。如果 Wasm 分配器的行为未知,则 roundUpToBucketSize() 应为恒等函数。

如果分配器的行为未知,您可能希望执行最多两个重新分配步骤,并将第一个重新分配步骤将剩余未转换长度乘以 2 而不是 3。但是,在这种情况下,不实现通常将已写入缓冲区长度乘以 2 是有意义的,因为在这种情况下,如果发生第二次重新分配,与原始长度乘以 3 相比,它将始终过度分配。以上建议假设您不需要为零终止符分配空间。也就是说,在 Wasm 端,您使用的是 Rust 字符串或非零终止的 C++ 类。如果您使用的是 C++ std::string,即使显示给您的逻辑长度,在计算向上舍入到分配器桶大小时也需要考虑额外的终止符字节。有关 C 字符串,请参阅下一节。

无零终止

如果输入字符串在输入中包含字符 U+0000,则 encodeInto() 将在输出中写入 0x00 字节。encodeInto()不会在逻辑输出后写入 C 样式的 0x00 哨兵字节。

如果您的 Wasm 程序使用 C 字符串,则您有责任写入 0x00 哨兵,并且如果 JavaScript 字符串包含 U+0000,则无法阻止您的 Wasm 程序看到逻辑上截断的字符串。观察

js
const encoder = new TextEncoder();

function encodeIntoWithSentinel(string, u8array, position) {
  const stats = encoder.encodeInto(
    string,
    position ? u8array.subarray(position | 0) : u8array,
  );
  if (stats.written < u8array.length) u8array[stats.written] = 0; // append null if room
  return stats;
}

示例

html
<p class="source">This is a sample paragraph.</p>
<p class="result"></p>
js
const sourcePara = document.querySelector(".source");
const resultPara = document.querySelector(".result");
const string = sourcePara.textContent;

const textEncoder = new TextEncoder();
const utf8 = new Uint8Array(string.length);

const encodedResults = textEncoder.encodeInto(string, utf8);
resultPara.textContent +=
  `Bytes read: ${encodedResults.read}` +
  ` | Bytes written: ${encodedResults.written}` +
  ` | Encoded result: ${utf8}`;

规范

规范
编码标准
# ref-for-dom-textencoder-encodeinto①

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅