Come leggere e scrivere bit in un array di byte
Domanda
Ho un buffer carattere senza segno e mi chiedo come scrivere e leggere i bit con e senza segno su questo buffer di byte.
Nel motore di origine esiste una classe denominata bf_write , che due metodi principali (utilizzati da WriteString, WriteChar, WriteLong, ecc.) utilizzano due funzioni denominate WriteUBitLong e WriteSBitLong .
Grazie in anticipo
Soluzione
Se il numero di bit è una costante di compilazione:
#include <bitset>
...
std::bitset<100> b;
b[2]=true;
In caso contrario, utilizzare Boost.dynamic_bitset
Oppure, se sei disperato, std :: vector, che in effetti è un bit pack impacchettato:
#include <vector>
...
std::vector<bool> b(100);
b[2]=true;
Sembra che tu voglia usare una libreria che richiede vettori di bit impacchettati in una matrice di byte. Senza sapere esattamente in quale ordine inserisce i bit, posso solo notare che:
1) tutto quanto sopra probabilmente utilizzerà almeno 32 bit ints con bit ordinati almeno - > most o most - > meno significativo
2) su CPU little endian (Intel / AMD), ciò significa che la memoria occupata dai byte una matrice di in potrebbe non essere coerente con l'ordinamento dei bit all'interno di int. se è " bit 0 è lsb di int 0, ... bit 32 è lsb di int 1, ... " allora è lo stesso in little endian di " bit 0 è lsb di char 0, ... bit 32 è lsb di char 4 ... " ;, nel qual caso puoi semplicemente lanciare un puntatore alla matrice int su un puntatore alla matrice di caratteri
3) supponendo che l'ordine nativo di byte nel tuo set di bit / vettore non sia esattamente ciò di cui la libreria ha bisogno, quindi devi creare il tuo che ha il layout che desidera o trascriverne una copia nel suo layout .
a) se l'ordine dei bit all'interno di un byte è diverso, una tabella di ricerca a 256 voci che fornisce il byte con bit invertiti sarebbe efficace. potresti generare la tabella con una piccola routine.
b) per invertire i byte dal piccolo < - > big endian:
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);
}
Per ottenere / impostare un determinato bit all'interno di una parola, con il bit # 0 nel bit meno significativo della parola 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);
}
Ovviamente se la regola per l'organizzazione dei bit è diversa, è necessario modificare quanto sopra.
Usando il più ampio possibile int la tua CPU elabora in modo efficiente come block_t è meglio (non dimenticare di cambiare block_bits
), a meno che l'endianness non si risolva con la libreria che stai usando.
Altri suggerimenti
Penso che siano sufficienti alcune macro:
#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)
Inoltre, lo scambio di endianness può essere fatto in modo più rapido. Ad esempio, per un numero intero a 64 bit v, le seguenti operazioni scambiano la sua endianness:
v = ((v & 0x00000000FFFFFFFFLLU) << 32) | (v >> 32);
v = ((v & 0x0000FFFF0000FFFFLLU) << 16) | ((v & 0xFFFF0000FFFF0000LLU) >> 16);
v = ((v & 0x00FF00FF00FF00FFLLU) << 8) | ((v & 0xFF00FF00FF00FF00LLU) >> 8);