2012-06-03 4 views
3

Мне нужно прочитать и записать числовые значения указанной длины бит (не обязательно кратные 8) с заданным битовым смещением в буфере памяти, самый старший бит сначала.чтение/запись значения при указанном смещении бита

Например, написав значение 5 по смещению 6 и бит длины 4:

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); 

Функции будут использоваться для чтения/записи небольшого количества значений в относительно большом буфере (f превышение сетевого трафика большого объема), поэтому я отбросил (возможно, ошибочно), используя std::bitset.

Существуют ли существующие библиотеки, которые могут быть использованы для достижения/упрощения этой задачи? Какие-либо предложения по его внедрению?

+0

Использование шаблонов для этих требований кажется мне бесполезным –

+0

@stefanbachert шаблоны имеют смысл, где код будет использоваться, но вы правы, они бесполезны здесь, поэтому я удалил их. – wroniasty

ответ

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 

Сообщите мне, если вы считаете это полезным, или у вас возникли проблемы с ним.

+0

thx @QuasarDonkey, основываясь на вашем ответе и некоторых моих других требованиях, я закодировал небольшую библиотеку для манипуляции бит. Если вам это интересно, здесь: http://github.com/wroniasty/bits. Приветствия. – wroniasty

+0

без проблем. и спасибо за кредит. – QuasarDonkey

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 

Не знаю ни библиотек, чтобы помочь с этим же.

+0

Это правильно, конечно, любые предложения по эффективному вычислению этих масок? – wroniasty