搜索字符串中第一个定界符的有效方法是什么?

What is an efficient way to search a string for the first of a set of delimiters?

我有一个 UTF-8 编码的字符串,我想遍历它, 将其拆分为多个定界符之一。我也需要知道 哪个个分隔符匹配,因为每个分隔符都有特定的含义。

用法示例:

algorithm("one, two; three") => Match("one")
algorithm(", two; three")    => Delimiter(",")
algorithm(" two; three")     => Match(" two")
algorithm("; three")         => Delimiter(";")
algorithm(" three")          => Match(" three")   

附加信息:

我的目标语言是 Rust,但我希望得到任何答案 具有类似的较低级别重点的语言。伪代码也很好, 只要它能识别 UTF-8 文本的现实。解决方案 使用深奥的十六进制技巧或 SIMD 指令也适用,但可能需要更多解释才能理解 ^_^。

对于特定于处理器的解决方案,具有 SSE4.2 的 X86-64 处理器包含 PCMPxSTRx 指令族。这些指令可用的模式之一是 Equal Any:

arg1 is a character set, arg2 is the string to search in. IntRes1[i] is set to 1 if arg2[i] is in the set represented by arg1

基本算法很简单:

  1. 用最多 16 个单字节填充 XMM 寄存器以搜索(指针)。
  2. 设置针字节数rax
  3. 计算字符串开头的内存地址,包括偏移量。
  4. rdx 中设置 haystack 字节数。
  5. 使用适当的控制字节调用 PCMPxSTRx
  6. 检查 ecx 或控制代码标志之一的结果。
  7. 如果没有匹配项并且仍有字符串要搜索,则增加偏移量并循环。

但是有a complication around page boundaries。即,PCMPxSTRx 指令将总是 读取 16 个字节的数据。如果您读入受保护的内存页,这可能会导致分段错误。一种解决方案是将所有读取对齐到字符串的 end,并处理开头的剩余字节。在开始上述算法之前,使用类似的东西:

  1. ~0xF屏蔽字符串的开头地址。这将清除所有低位。
  2. 对前 16 个字节使用 PCMPxSTRM 指令(具有与上述算法类似的设置)。这 returns 匹配字符的掩码。您可以移动掩码以忽略不属于您的字符串的前导字符。
  3. 如果没有匹配项并且还有更多字符串需要搜索,则开始上述算法。

你可以在我的Rust library Jetscii中看到这个算法的完整例子。内联汇编用于调用 PCMPxSTRx 指令。