3

我需要在内存缓冲区中的指定位偏移处读取和写入指定位长(不一定是 8 的倍数)的数值,首先是最重要的位。

例如,在偏移量 6 和位长 4 处写入值 5:

before: 11111111 11111111
bits:         ^^ ^^
after:  11111101 01111111

所以我正在寻找的功能可能是这样定义的:

unsigned get_bits (unsigned char *buffer, int offset, int bit_size);
void set_bits (unsigned char *buffer, int offset, int bit_size, 
                             unsigned value);

以及示例用法:

set_bits (buffer, 6, 4, 5);
unsigned v = get_bits (buffer, 6, 4);
assert (v == 5);

这些函数将用于在相对较大的缓冲区中读取/写入少量值(过滤大量网络流量),因此我丢弃(可能是错误地)使用std::bitset.

是否有任何现有的库可用于实现/简化此任务?关于实施它有什么建议吗?

4

2 回答 2

2

摆弄比特很少容易。这是一个完整的工作示例,说明如何做你想做的事。

这里的大问题是跨字节边界读取和写入数字块。如果你把它们分成一口大小的块,问题总是更容易,如果你能原谅双关语。

首先,我创建了一个像 std::bitset 这样可以包装用户定义缓冲区的类。它让我们可以在二进制数据的大缓冲区中摆弄各个位。

#include <cassert>  // for assert
#include <cstring>  // for memset

// A wrapper for a data buffer providing bit-level access.
class BitBuffer {
public:
    BitBuffer (unsigned char *src_buffer, size_t byte_size)
        : m_data( src_buffer ), m_size( byte_size )
    {
        if (m_size) assert(m_data);
    }

    // Clear the buffer (set all bits to 0).
    void clear () {
        memset( m_data, 0, m_size );
    }

    // Get an individual bit's value.
    bool get (size_t bitpos) const {
        assert( bitpos / 8 < m_size );
        return m_data[ bitpos / 8 ] & ( 1 << ( bitpos % 8 ) );
    }

    // Set an individual bit's value.
    void set (size_t bitpos, bool val=true) {
        assert( bitpos / 8 < m_size );
        if( val ) {
            m_data[ bitpos / 8 ] |= ( 1 << ( bitpos % 8 ) );
        } else {
            m_data[ bitpos / 8 ] &= ~( 1 << ( bitpos % 8 ) );
        }
    }

    // Switch off a bit.
    void reset (size_t bitpos) { 
        set( bitpos, false );
    }

    // Flip a bit.
    void flip (size_t bitpos) {
        set( bitpos, ! get( bitpos ) );
    }

    // Return the size of the buffer in bytes.
    size_t byte_size () const { return m_size; }

    // Return the size of the buffer in bits.
    size_t bit_size () const { return m_size * 8; }

    // Return a const pointer to the buffer.
    unsigned char const *  buffer () const { return m_data; }

private:
    unsigned char * m_data;
    size_t m_size;
};

然后我编写了一些函数来从缓冲区中获取和设置位块,首先是 MSB。

unsigned get_bits (BitBuffer& buffer, size_t offset, size_t bit_size)
{
    unsigned bits = 0;
    for (size_t i = 0; i < bit_size; i++) {
        // We reverse the order of the bits, so the first bit read
        // from the buffer maps to the high bit in 'bits'.
        bits |= ( buffer.get( offset + i ) << (bit_size - 1 - i) );
    }
    return bits;
}

void set_bits (BitBuffer& buffer, size_t offset, size_t bit_size, unsigned bits)
{
    for (size_t i = 0; i < bit_size; i++) {
        // We reverse the order of the bits, so the high bit of 'bits'
        // gets written to the buffer first.
        bool bit_value = bits & ( 1 << (bit_size - 1 - i) );
        buffer.set( offset + i, bit_value );
    }
}

和一个测试工具:

#include <cstdio>

// Print the bits of the buffer to stdout.
void dump_buffer (BitBuffer& buffer)
{
    for (size_t i = 0; i < buffer.bit_size(); i++) {
        printf( "%i", buffer.get(i) );
    }
    printf("\n");
}

int main()
{
    const size_t byte_size = 4;  // size of buffer in bytes
    unsigned char * raw_buffer = new unsigned char[ byte_size ];

    BitBuffer buffer( raw_buffer, byte_size );
    buffer.clear();

    printf("Test #0: contents of 4-byte buffer:\n");

    // Show the buffer.
    dump_buffer( buffer );

    printf("\nTest #1: setting and flipping bits:\n");

    // Set some bits
    buffer.set( 5 );
    buffer.set( 10 );
    buffer.set( 12 );
    buffer.set( 31 );

    // Show the buffer.
    dump_buffer( buffer );

    // Read some bits.
    printf( "Value at 12 before flip: %i\n", buffer.get( 12 ) );
    buffer.flip( 12 );
    printf( "Value at 12 after flip: %i\n", buffer.get( 12 ) );

    printf("\nTest #2: setting all 1's, and writing 5, length 4 to offset 6:\n");

    // Fill with 1's.
    set_bits(buffer, 0, 32, 0xFFFFFFFF);

    // Set 5 at offset 6, bit size 4.
    set_bits(buffer, 6, 4, 5);
    assert( get_bits(buffer, 6, 4) == 5 );

    // Show the buffer.
    dump_buffer( buffer );

    // All done.
    delete raw_buffer;
    return 0;
}

要编译,只需将所有这些转储到一个文件中并编译。测试运行的输出:

Test #0: contents of 4-byte buffer:
00000000000000000000000000000000

Test #1: setting and flipping bits:
00000100001010000000000000000001
Value at 12 before flip: 1
Value at 12 after flip: 0

Test #2: setting all 1's, and writing 5, length 4 to offset 6:
11111101011111111111111111111111

如果您觉得它有用,或者您有任何问题,请告诉我。

于 2012-06-04T00:55:40.677 回答
1

要将特定位设置为 1,您可以创建一系列全为 0 的位,除了您要设置的位(那些为 1)和OR现有位。要将特定位设置为 0,您只需执行相同的操作,所有位都是 1,除了您想要 0 的位并且您使用AND运算符。

例子:

before:        11111111 11111111
bits to SET:         _^ _^       5 is 0101, so we SET just the 1 bits, _ is placeholder
bitmask: OR    00000001 01000000
result:        11111111 11111111 No different because they were all 1s already.
bits to RESET:       ^_ ^_       RESET the 0s
bitmask: AND   11111101 01111111
result:        11111101 01111111

不知道有任何图书馆可以帮助解决这个问题。

于 2012-06-03T16:38:46.147 回答