2

unsigned charバッファがあり、このバイトバッファに符号付きビットと符号なしビットをどのように読み書きするのか疑問に思っています。

ソースエンジンにはbf_writeという名前のクラスがあり、2つの主要なメソッド(WriteString、WriteChar、WriteLongなどで使用)はWriteUBitLongWriteSBitLongという名前の2つの関数を使用します。

前もって感謝します

4

2 に答える 2

10

ビット数がコンパイル時定数の場合:

#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ビットのintを使用し、ビットの順序は最下位->最上位または最下位->最下位になります。

2)リトルエンディアン(Intel / AMD)CPUでは、これは、intの配列のバイトが占めるメモリがint内のビットの順序と一致しない可能性があることを意味します。「ビット0はint0のlsb、...ビット32はint 1のlsb、...」の場合、リトルエンディアンでは「ビット0はchar 0のlsb、...ビット」と同じです。 32はchar4... "のlsbです。この場合、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_bits使用しているライブラリでエンディアンが機能しない場合を除いて、block_tが最適であるため(変更することを忘れないでください)、CPUが可能な限り広いintを効率的に使用します。

于 2009-09-12T18:26:08.027 に答える
1

いくつかのマクロで十分だと思います。

#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);
于 2009-09-12T22:36:02.587 に答える