我最近遇到了一个有趣的问题:
假设我有一个长度至少为1的字节数组(确切地说是uint8_t)。现在我需要一个函数来获取这个数组的位子序列,从位X(基于零的索引,包括端点)开始,长度为L,并将其作为uint32_t返回。如果L小于32,则剩余的高位应为零。
虽然这不是很难解决,但我目前对如何做到这一点的想法对我来说似乎有点麻烦。我正在考虑一个给定字节的所有可能掩码的表(从0-7位开始,取1-8位),然后使用该表一次构造一个字节。
有人可以提出更好的解决方案吗?请注意,我不能使用Boost或STL - 不,它不是作业,它是我在工作中遇到的问题,我们不会在代码中使用Boost或STL。你可以假设:0< L< = 32并且字节数组足够大以保持子序列。
正确输入/输出的一个例子:
阵列:00110011 1010 1010 11110011 01 101100
子序列:X = 12(零基指数),L = 14
结果uint32_t = 00000000 00000000 00 101011 11001101
答案 0 :(得分:4)
只有子序列中的第一个和最后一个字节将涉及一些位切片以获得所需的位,而中间字节可以整体移位到结果中。这里有一些示例代码,绝对未经测试 - 它完成了我所描述的内容,但有些位索引可能会被一个关闭:
uint8_t bytes[];
int X, L;
uint32_t result;
int startByte = X / 8, /* starting byte number */
startBit = 7 - X % 8, /* bit index within starting byte, from LSB */
endByte = (X + L) / 8, /* ending byte number */
endBit = 7 - (X + L) % 8; /* bit index within ending byte, from LSB */
/* Special case where start and end are within same byte:
just get bits from startBit to endBit */
if (startByte == endByte) {
uint8_t byte = bytes[startByte];
result = (byte >> endBit) & ((1 << (startBit - endBit)) - 1);
}
/* All other cases: get ending bits of starting byte,
all other bytes in between,
starting bits of ending byte */
else {
uint8_t byte = bytes[startByte];
result = byte & ((1 << startBit) - 1);
for (int i = startByte + 1; i < endByte; i++)
result = (result << 8) | bytes[i];
byte = bytes[endByte];
result = (result << (8 - endBit)) | (byte >> endBit);
}
答案 1 :(得分:1)
看看std :: bitset和boost :: dynamic_bitset。
答案 2 :(得分:1)
我会想到像装载一个uint64_t一样,然后左右移动以失去无趣的位。
uint32_t extract_bits(uint8_t* bytes, int start, int count)
{
int shiftleft = 32+start;
int shiftright = 64-count;
uint64_t *ptr = (uint64_t*)(bytes);
uint64_t hold = *ptr;
hold <<= shiftleft;
hold >>= shiftright;
return (uint32_t)hold;
}
答案 3 :(得分:1)
为了完整性,我在这里添加了我的解决方案,其灵感来自于评论和答案。感谢所有想到这个问题的人。
static const uint8_t firstByteMasks[8] = { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
uint32_t getBits( const uint8_t *buf, const uint32_t bitoff, const uint32_t len, const uint32_t bitcount )
{
uint64_t result = 0;
int32_t startByte = bitoff / 8; // starting byte number
int32_t endByte = ((bitoff + bitcount) - 1) / 8; // ending byte number
int32_t rightShift = 16 - ((bitoff + bitcount) % 8 );
if ( endByte >= len ) return -1;
if ( rightShift == 16 ) rightShift = 8;
result = buf[startByte] & firstByteMasks[bitoff % 8];
result = result << 8;
for ( int32_t i = startByte + 1; i <= endByte; i++ )
{
result |= buf[i];
result = result << 8;
}
result = result >> rightShift;
return (uint32_t)result;
}
很少有人注意到:我测试了代码并且似乎工作正常,但是,可能存在错误。如果我找到任何,我会在这里更新代码。此外,可能有更好的解决方案!