如何在字节数组中读取和写入位

发布于 2024-08-04 03:43:14 字数 294 浏览 4 评论 0原文

我有一个 unsigned char 缓冲区,我想知道如何向该字节缓冲区写入和读取有符号和无符号位。

Source Engine中有一个名为bf_write的类,其中两个主要方法(由WriteString、WriteChar、WriteLong等使用)使用两个名为WriteUBitLong<的函数/strong> 和 WriteSBitLong

提前致谢

I have a unsigned char buffer, and I'm wondering how I would write and read signed and unsigned bits to this byte buffer.

In the Source Engine there is a class named bf_write, which two main methods (used by WriteString, WriteChar, WriteLong, etc.) use two functions named WriteUBitLong and WriteSBitLong.

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

假装不在乎 2024-08-11 03:43:14

如果位数是编译时常量:

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

如果不是,请使用

,std::vector,它确实是一个打包的位向量:

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

你似乎想要使用一个需要将位向量打包在字节数组中的库。在不确切知道它以什么顺序放置位的情况下,我只能注意到:

1)以上所有内容可能会使用至少 32 位整数,其中位顺序为最低 - >最高或最高 - >最低有效

2) Little Endian (Intel/AMD) CPU,这意味着 int 数组的字节占用的内存可能与 int 内的位顺序不一致。如果它是“位 0 是 int 0 的 lsb,...位 32 是 int 1 的 lsb,...”那么在小端中与“位 0 是 char 0 的 lsb,...位”相同32 是 char 4 ..." 的 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);
}

显然,如果位组织的规则不同,则必须改变上面的。

使用尽可能宽的 int 有效地处理 CPU,因为 block_t 是最好的(不要忘记更改 block_bits),除非字节序无法与您正在使用的库配合使用。

If the number of bits is a compile-time constant:

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

If it's not, use Boost.dynamic_bitset

Or, if you're desperate, std::vector, which is indeed a packed bit vector:

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

You seem to want to use a library that requires bit vectors packed in an array of bytes. Without knowing exactly what order it places the bits in, I can only note that:

1) all of the above will probably use at least 32-bit ints with bits ordered least->most or most->least significant

2) on little endian (Intel/AMD) CPUs, this means that the memory occupied by the bytes an array of ints may not be consistent with the ordering of bits within the int. if it's "bit 0 is the lsb of int 0, ... bit 32 is the lsb of int 1, ..." then that's the same in little endian as "bit 0 is the lsb of char 0, ... bit 32 is the lsb of char 4 ...", in which case you can just cast a pointer to the int array to a pointer to char array

3) supposing the native order of bytes in your bit set / vector isn't exactly what the library needs, then you have to either have to create your own that has the layout they want, or transcribe a copy into their layout.

a) if the order of bits within a byte is different, a 256 entry lookup table giving the byte with bits reversed would be efficient. you could generate the table with a small routine.

b) to reverse bytes from little<->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);
}

To get/set a particular bit within a word, with bit #0 in the least significant bit of word 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);
}

Obviously if the rule for bit organization differs, you have to change the above.

Using the widest possible int your CPU processes efficiently as block_t is best (dont' forget to change block_bits), unless the endianness doesn't work out w/ the library you're using.

不气馁 2024-08-11 03:43:14

我认为几个宏就足够了:

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

I think a few macros are enough:

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

In addition, swapping endianness can be done in a faster way. For example, for a 64-bit integer v, the following operations swap its endianness:

v = ((v & 0x00000000FFFFFFFFLLU) << 32) | (v >> 32);
v = ((v & 0x0000FFFF0000FFFFLLU) << 16) | ((v & 0xFFFF0000FFFF0000LLU) >> 16);
v = ((v & 0x00FF00FF00FF00FFLLU) << 8) | ((v & 0xFF00FF00FF00FF00LLU) >> 8);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文