我正在寻找一种方法来有效地将位插入比特流并使其“溢出”,用0填充。
因此,例如,如果你有一个2字节的字节数组:231和109(11100111 01101101),并且BitInsert(byteArray,4,00)它会在位偏移4处插入两位,使得11100001 11011011 01000000(225,219, 24)。即使该方法仅允许1位插入也可以,例如BitInsert(byteArray,4,true)或BitInsert(byteArray,4,false),但该方法必须独立于比特流长度(流可能跨越数百个字节)。
我有一种方法可以做到这一点,但它必须一点一点地使用位掩码来处理流,所以我想知道是否有更简单的方法......
汇编或C衍生产品的答案将不胜感激。
编辑:特定用例是一种编码方案的实现,它一次读取6位字节数组,并将它们(用2位填充)编码为单字节。所以每6位,你插入2位。 {33,66,99}作为比特流 001000010100001001100011成为 00001000000101000000100100100011注意插入为xx: xx001000xx010100xx001001xx100011
我希望有一种方法可以做到这一点而不用步行...... (如果有人知道这个编码方案的官方名称,那将会有所帮助,因为我还没有确定它...当将一个较旧的C程序移植到C#中时会出现这种情况。)
答案 0 :(得分:2)
我在监督测试时有一个小时的时间,这就是结果:
class BitStream
{
private List<byte> _bytes;
public BitStream()
{
_bytes = new List<byte>();
_bytes.Add(0);
}
public byte[] Data
{
get { return _bytes.ToArray(); }
}
public void Insert(int index, int value)
{
if (value < 0)
value *= -1;
int bit = value % 2;
byte bitmask = GetBitmask(index, bit);
// perform the right-shift operation
int active_byte = index / 8;
bool padded = PadIfNecessary();
int i;
if (padded)
i = _bytes.Count - 2;
else
i = _bytes.Count - 1;
for ( ; i > active_byte; --i)
{
_bytes[i] = (byte)(_bytes[i] << 1);
// carry from earlier byte if necessary
if ((_bytes[i - 1] & 128) == 128)
_bytes[i] |= 1;
}
// now shift within the target byte
_bytes[active_byte] = ShiftActiveByte(_bytes[active_byte], index);
_bytes[active_byte] |= GetBitmask(index, bit);
}
protected byte ShiftActiveByte(byte b, int index)
{
index = index % 8;
byte low_mask = 0;
byte high_mask = 255;
for (int i=0; i<index; ++i)
{
low_mask = (byte)((low_mask << 1) | 1);
high_mask = (byte)(high_mask << 1);
}
byte low_part = (byte)(b & low_mask);
byte high_part = (byte)(b & high_mask);
high_part <<= 1;
return (byte)(low_part | high_part);
}
protected byte GetBitmask(int index, int value)
{
return (byte)(value << (index % 8));
}
protected bool PadIfNecessary()
{
if ((_bytes[_bytes.Count - 1] & 128) == 128)
{
_bytes.Add(1);
return true;
}
else return false;
}
}
它不会处理超出内部数组现有边界的索引处的插入,而是在我的非正式冒烟测试中正确处理自己。
答案 1 :(得分:1)
如果您知道您的输出适合类似于int32或int64的内容,则可以使用bitshift运算符&gt;&gt;。
答案 2 :(得分:0)
有效的选择将在很大程度上取决于您的瓶颈所在。由于您具体询问插入,我假设您的应用程序需要进行大量的随机访问插入,并且实际上只需要偶尔读取完整的流。如果是这种情况,这里有一些可能的选择:
选项1:字节链接列表
struct BitStreamNode;
{
Byte size;
Byte bits;
BitStreamNode * next;
};
如果您可以维护指向要插入位的节点的指针,这将最有效。如果必须将插入点指定为数字索引,请参阅选项2.请注意,我已包含一个size成员。这将允许您按如下方式插入两位:
BitStreamNode newNode;
newNode.bits = 0x02; // for example
newNode.size = 2;
newNode.next = nodeToInsertAfter.next;
nodeToInsertAfter.next = newNode;
在现有节点的中间插入当然需要将节点分成两部分。同样,让我强调一点,如果你经常这样做,并且b)你的流中有大量的比特,那么这只会比将整个数组向右移动更有效。
选项2:平衡树结构
此方法将使用选项1中概述的类似节点,但会在每个节点处包含数字索引,以及指向较高和较低索引节点的链接。基本思路是binary search tree,它减少了查找流中特定节点的确切位置所需的时间,但增加了到下一个和前一个节点的有序链接(以便能够按顺序读取流。)
更新: 确切的定义似乎是“threaded tree”。
这个需要大量的工作才能正确实施,所以如果你绝对确定速度增益是值得的话,我只建议走这条路。即:描述基本的暴力解决方案,然后衡量额外努力和复杂性的利弊。