为特定的半字节创建掩码

Creating masks for specific nibbles

如何以尽可能高效的方式为无符号 64 位整数中与特定值匹配的所有半字节创建掩码?

例如,假设我有一个 64 位无符号整数:

0000 0100 0011 0011 0011 0011 0010 0010 0010 0010 0010 0010 0001 0001 0001 0001 

并说我只想允许值为 0010 的半字节。 我怎样才能找到那些半字节并为它们创建掩码。 在这个人为的例子中,我当然知道半字节 5:10 是 0010,因此要创建的相应掩码是:

0000 0000 0000 0000 0000 0000 1111 1111 1111 1111 1111 1111 0000 0000 0000 0000

但我想为任何 64 位无符号整数和任何半字节值创建这样的掩码。例如,我可能对 010000101000 半字节感兴趣。

例如如下(只是众所周知的技巧组合)

  1. XOR 与所需的值,使半字节为 0 iff​​ 它具有正确的值
  2. 计算所有半字节的水平或
  3. 删除垃圾位
  4. 扩大结果以适应整个半字节
  5. 面具最终倒置,将其倒置

所以,未测试:

x ^= test_value
// now h-OR nibbles
x |= x >> 1
x |= x >> 2
// remove junk
x &= 0x1111111111111111
// widen
x *= 15
// invert
x = ~x

这是我经过数小时的反复试验设法得出的版本,但远没有哈罗德的版本那么整洁。我post这里只是为了完整性: 我用julia语言写的。

@inline function mask_nibbles(x::UInt64, value::UInt64)
    x = ~(x $ value)
    x = (x & 0x1111111111111111) &
    ((x >> 1) & 0x1111111111111111) &
    ((x >> 2) & 0x1111111111111111) &
    ((x >> 3) & 0x1111111111111111)
    return x | (x << 1) | (x << 2) | (x << 3)
end