从具有位偏移的 std::string bytewise 中提取连续位

Extracting continuos bits from a std::string bytewise with a bit offset

我有点不知所措,我想从字符串(来自网络)中提取具有定义的位偏移量和位长度(unsigned long long)的最多 64 位。

字符串的长度可以是未定义的,所以我需要确保只按字节访问它。 (也意味着我不能使用 _bextr_u32 intrinsic)。我不能使用 std 位集 class 因为它不允许使用偏移量提取多于一位并且也只允许提取预定义的位数。

所以我已经计算了字节偏移量(在字符串内)和位偏移量(在起始字节内)。

m_nByteOffset = nBitOffset / 8;
m_nBitOffset = nBitOffset % 8;

现在我可以得到起始地址了

const char* sSource = str.c_str()+m_nByteOffset;

和位掩码

unsigned long long nMask = 0xFFFFFFFFFFFFFFFFULL >> (64-nBitLen);

但现在我不知道如何从中提取最多 64 位,因为没有可用的 128 位整数。

unsigned long long nResult = ((*(unsigned long long*)sSource) >> m_nBitOffset) & nMask;

这仅适用于最多 64 位偏移位,我如何才能将其扩展为真正适用于 64 位且独立于位偏移。而且由于这不是按字节访问,它可能会导致内存读取访问冲突。

所以我真的在寻找一个字节解决方案来解决这个问题,最多适用于 64 位。 (最好是 C 或内在函数)

更新:经过大量搜索和测试后,我可能会使用 RakNet 中的这个功能: https://github.com/OculusVR/RakNet/blob/master/Source/BitStream.cpp#L551

这就是我在现代 C++ 风格中的做法。 位长度由缓冲区的大小决定 extractedBits:除了使用 unsigned long long,您还可以使用具有所需大小的任何其他数据类型(甚至数组类型)。

See it live

unsigned long long extractedBits;
char* extractedString = reinterpret_cast<char*>(&extractedBits);
std::transform(str.begin() + m_nByteOffset,
               str.begin() + m_nByteOffset + sizeof(extractedBits),
               str.begin() + m_nByteOffset + 1,
               extractedString,
               [=](char c, char d)
               {
                   char bitsFromC = (c << m_nBitOffset);
                   char bitsFromD = 
                       (static_cast<unsigned char>(d) >> (CHAR_BIT - m_nBitOffset));
                   return bitsFromC | bitsFromD;
               });

要按字节执行,只需一次读取一个字节的字符串(顺便说一句,最好将其解释为 uint8_t 而不是 char 的序列),更新结果通过将它左移 8 并 or 将其与当前字节相结合。唯一复杂的是第一位和最后一位,它们都需要您读取一个字节的一部分。对于第一部分,只需使用一个位掩码来获得您需要的位,对于最后一部分,将其向下移动所需的数量。这是代码:

const uint8_t* sSource = reinterpret_cast<const uint8_t*>(str.c_str()+m_nByteOffset);

uint64_t result = 0;
uint8_t FULL_MASK = 0xFF;

if(m_nBitOffset) {
    result = (*sSource & (FULL_MASK >> m_nBitOffset));
    nBitLen -= (8 - m_nBitOffset);
    sSource++;
}

while(nBitLen > 8) {
    result <<= 8;
    result |= *sSource;
    nBitLen -= 8;
    ++sSource;
}

if(nBitLen) {
    result <<= nBitLen;
    result |= (*sSource >> (8 - nBitLen));
}

return result;