插入比特流

时间:2010-04-13 23:42:52

标签: c# c++ c

我正在寻找一种方法来有效地将位插入比特流并使其“溢出”,用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#中时会出现这种情况。)

3 个答案:

答案 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;。

  1. 使用预定义的一组掩码将输入流分成两部分。
  2. 使用&gt;&gt;移动尾端的位数等于所需插入的长度。
  3. 对你的插件做同样的事情。
  4. 使用|操作员将所有3件组合在一起。

答案 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”。

这个需要大量的工作才能正确实施,所以如果你绝对确定速度增益是值得的话,我只建议走这条路。即:描述基本的暴力解决方案,然后衡量额外努力和复杂性的利弊。