Как читать и записывать биты в байтовый массив - PullRequest
2 голосов
/ 12 сентября 2009

У меня есть беззнаковый буфер char , и мне интересно, как бы я записывал и считывал биты со знаком и без знака в этот байтовый буфер.

В Source Engine есть класс с именем bf_write , в котором два основных метода (используются WriteString, WriteChar, WriteLong и т. Д.) Используют две функции с именем WriteUBitLong и WriteSBitLong .

Заранее спасибо

Ответы [ 2 ]

10 голосов
/ 12 сентября 2009

Если число битов является постоянной времени компиляции:

#include <bitset>
...
std::bitset<100> b;
b[2]=true;

Если это не так, используйте Boost.dynamic_bitset

Или, если вы в отчаянии, std :: vector, который действительно является упакованным битовым вектором:

#include <vector>
...
std::vector<bool> b(100);
b[2]=true;

Похоже, вы хотите использовать библиотеку, которая требует битовые векторы, упакованные в массив байтов. Не зная точно, в каком порядке он размещает биты, я могу только отметить, что:

1) все вышеперечисленное, вероятно, будет использовать, по крайней мере, 32-битные целые с упорядоченными битами наименьший -> самый или самый -> наименее значимый

2) на процессорах с прямым порядком байтов (Intel / AMD) это означает, что память, занимаемая байтами массива целых чисел, может не соответствовать порядку битов в int. если это "бит 0 - это lsb из int 0, ... бит 32 - это lsb из int 1, ...", то это то же самое для little endian, что и "бит 0 - это lsb из char 0, ... bit 32 - это символ char 4 ... ", в этом случае вы можете просто привести указатель на массив int к указателю на массив char

3) предположим, что родной порядок байтов в вашем наборе битов / векторе не совсем то, что нужно библиотеке, тогда вам нужно либо создать свой собственный, имеющий желаемый макет, либо записать копию в свой макет ,

a) если порядок битов внутри байта отличается, то таблица поиска с 256 входами, предоставляющая байт с обращенными битами, будет эффективной. Вы можете создать таблицу с помощью небольшой процедуры.

b) для обращения байтов от младшего <-> старшего байта:

inline void endian_swap(unsigned short& x)
{
    x = (x>>8) | 
        (x<<8);
}

inline void endian_swap(unsigned int& x)
{
    x = (x>>24) | 
        ((x<<8) & 0x00FF0000) |
        ((x>>8) & 0x0000FF00) |
        (x<<24);
}    

inline void endian_swap(unsigned long long& x)
{
    x = (x>>56) | 
        ((x<<40) & 0x00FF000000000000) |
        ((x<<24) & 0x0000FF0000000000) |
        ((x<<8)  & 0x000000FF00000000) |
        ((x>>8)  & 0x00000000FF000000) |
        ((x>>24) & 0x0000000000FF0000) |
        ((x>>40) & 0x000000000000FF00) |
        (x<<56);
}

Чтобы получить / установить определенный бит в слове с битом 0 в младшем значащем бите слова 0:

typedef unsigned char block_t;
const unsigned block_bits=8;

inline void set_bit(block_t *d,unsigned i) {
  unsigned b=i/block_bits;
  unsigned bit=i-(block_bits*b); // same as i%b
  block_t &bl=d[b];
  bl|=(1<<bit); // or bit with 1 (others anded w/ 0)
}

inline void clear_bit(block_t *d,unsigned i) {
  unsigned b=i/block_bits;
  unsigned bit=i-(block_bits*b); // same as i%b
  block_t &bl=d[b];
  bl&=(~(1<<bit)); // and bit with 0 (other bits anded w/ 1)
}

inline void modify_bit(block_t *d,unsigned i,bool val) {
  if (val) set_bit(d,i) else clear_bit(d,i);
}

inline bool get_bit(block_t const* d,unsigned i) {
  unsigned b=i/block_bits;
  unsigned bit=i-(block_bits*b); // same as i%b
  return d[b]&(1<<bit);
}

Очевидно, что если правило для организации битов отличается, вы должны изменить вышеприведенное.

Эффективно использовать максимально широкий процесс ЦП, так как block_t лучше всего (не забывайте менять block_bits), если только не работает последовательность с библиотекой, которую вы используете.

1 голос
/ 13 сентября 2009

Я думаю, что достаточно нескольких макросов:

#define set_bit0(buf, i) ((buf)[(i)/8]&=~(1u<<(i)%8))
#define set_bit1(buf, i) ((buf)[(i)/8]|=1<<(i)%8)
#define get_bit(buf, i) ((buf)[(i)/8]>>(i)%8&1)

Кроме того, обмен порядком байтов может быть выполнен быстрее. Например, для 64-разрядного целого числа v следующие операции меняют свой порядок байтов:

v = ((v & 0x00000000FFFFFFFFLLU) << 32) | (v >> 32);
v = ((v & 0x0000FFFF0000FFFFLLU) << 16) | ((v & 0xFFFF0000FFFF0000LLU) >> 16);
v = ((v & 0x00FF00FF00FF00FFLLU) << 8) | ((v & 0xFF00FF00FF00FF00LLU) >> 8);
...