C++ 从缓冲区中删除第n位,并移动其余的

C++ 从缓冲区中删除第n位,并移动其余的,c++,c,bit-manipulation,C++,C,Bit Manipulation,给定长度为x的uint8\t缓冲区,我试图想出一个函数或宏,可以删除第n位(或n到n+I),然后左移剩余的位 示例#1: 对于输入0b76543210 0b76543210…,则输出应为0b76543217 0b654321… 示例2:如果输入为: uint8_t input[8] = { 0b00110011, 0b00110011, ... }; 不带第一位的输出应为 uint8_t output[8] = { 0b00110010, 0b01100

给定长度为
x
uint8\t
缓冲区,我试图想出一个函数或宏,可以删除第n位(或n到n+I),然后左移剩余的位

示例#1:

对于输入
0b76543210 0b76543210…
,则输出应为
0b76543217 0b654321…

示例2:如果输入为:

uint8_t input[8] = {
    0b00110011,
    0b00110011,
    ...
};
不带第一位的输出应为

uint8_t output[8] = {
    0b00110010,
    0b01100100,
    ...
};
我尝试了以下方法来删除第一个位,但对第二组位无效

/* A macro to extract (a-b) range of bits without shifting */
#define BIT_RANGE(N,x,y) ((N) & ((0xff >> (7 - (y) + (x))) << ((x))))
void removeBit0(uint8_t *n) {
    for (int i=0; i < 7; i++) {
        n[i] = (BIT_RANGE(n[i], i + 1, 7)) << (i + 1) |
               (BIT_RANGE(n[i + 1], 1, i + 1)) << (7 - i); /* This does not extract the next element bits */
    }
    n[7] = 0;
}
/*在不移位的情况下提取(A-b)位范围的宏*/

#定义位范围(N,x,y)((N)和((0xff>>(7-(y)+(x)))类似于此的东西应该可以工作:

template<typename S> void removeBit(S* buffer, size_t length, size_t index)
{
  const size_t BITS_PER_UNIT = sizeof(S)*8;

  // first we find which data unit contains the desired bit
  const size_t unit = index / BITS_PER_UNIT;
  // and which index has the bit inside the specified unit, starting counting from most significant bit
  const size_t relativeIndex = (BITS_PER_UNIT - 1) - index % BITS_PER_UNIT;

  // then we unset that bit
  buffer[unit] &= ~(1 << relativeIndex);

  // now we have to shift what's on the right by 1 position
  // we create a mask such that if 0b00100000 is the bit removed we use 0b00011111 as mask to shift the rest
  const S partialShiftMask = (1 << relativeIndex) - 1;

  // now we keep all bits left to the removed one and we shift left all the others
  buffer[unit] = (buffer[unit] & ~partialShiftMask) | ((buffer[unit] & partialShiftMask) << 1);

  for (int i = unit+1; i < length; ++i)
  {
    //we set rightmost bit of previous unit according to last bit of current unit
    buffer[i-1] |= buffer[i] >> (BITS_PER_UNIT-1);
    // then we shift current unit by one
    buffer[i] <<= 1;
  }
}
模板void removeBit(S*缓冲区、大小长度、大小索引)
{
常数大小每单位比特数=sizeof(S)*8;
//首先,我们找到包含所需位的数据单元
常量大小单位=每单位的索引/位;
//哪个索引的位在指定的单位内,从最高有效位开始计数
const size_t relativeIndex=(每单位比特数-1)-索引%BITS_每单位;
//然后,我们取消了这一点

buffer[unit]&=~(1这实际上是两个子问题:从每个字节中删除位并打包结果。这是下面的代码流程。我不会为此使用宏。进行了太多操作。如果您担心该级别的性能,只需内联函数即可

#include <stdio.h>
#include <stdint.h>

// Remove bits n to n+k-1 from x.
unsigned scrunch_1(unsigned x, int n, int k) {
  unsigned hi_bits = ~0u << n;
  return (x & ~hi_bits) | ((x >> k) & hi_bits);
}

// Remove bits n to n+k-1 from each byte in the buffer,
// then pack left. Return number of packed bytes.
size_t scrunch(uint8_t *buf, size_t size, int n, int k) {
  size_t i_src = 0, i_dst = 0;
  unsigned src_bits = 0; // Scrunched source bit buffer.
  int n_src_bits = 0;    // Initially it's empty.
  for (;;) {
    // Get scrunched bits until the buffer has at least 8.
    while (n_src_bits < 8) {
      if (i_src >= size) { // Done when source bytes exhausted.
        // If there are left-over bits, add one more byte to output.
        if (n_src_bits > 0) buf[i_dst++] = src_bits << (8 - n_src_bits);
        return i_dst;
      }
      // Pack 'em in.
      src_bits = (src_bits << (8 - k)) | scrunch_1(buf[i_src++], n, k);
      n_src_bits += 8 - k;
    }
    // Write the highest 8 bits of the buffer to the destination byte.
    n_src_bits -= 8;
    buf[i_dst++] = src_bits >> n_src_bits;
  }
}

int main(void) {
  uint8_t x[] = { 0xaa, 0xaa, 0xaa, 0xaa };
  size_t n = scrunch(x, 4, 2, 3);
  for (size_t i = 0; i < n; i++) {
    printf("%x ", x[i]);
  }
  printf("\n");
  return 0;
}

这写的是
D65A b
。其他一些测试用例也可以使用。

在x86上,有一条说明(等等,什么?):
pdep/pext
,由
BMI2
指令集扩展添加。谢谢,这很有趣,我正在尝试一下。我对这个问题感到有点困惑。图表表明,您不只是想删除一位,实际上还想从由x八位字节数组表示的位流中删除位s+8*I,其中s在[0,7],也就是说,这是位流压缩的一种特殊形式。一种简单的方法是简单地循环输入位,保持源和目标位的独立运行计数,以指示何时抑制位复制以及何时检索/存放下一个八位字节。对于您的目的来说,这会太慢吗?如果假设不会o作为“跨平台”,为什么要使用“汇编”和“内联汇编”标记?此外,您还没有回答njuffa关于您的图表如何与您的问题相矛盾的问题。问题的第一行表示您要删除一位(或一系列连续位)从缓冲区中删除。图表显示多个非连续位被删除。您必须决定您的问题是要求跨平台解决方案还是平台特定解决方案。在前一种情况下,您不适合发布平台特定的答案,在后一种情况下,您需要命名平台。谢谢,这实际上是可行的,我想指出endian的问题,但是你已经提到了,所以,谢谢你。非常感谢你,我喜欢你花时间提供模板函数的方式,但不幸的是,这并没有产生所需的输出,我正试图修改代码一点,以便使用uint8\t缓冲区。
#include <stdio.h>
#include <stdint.h>

// Remove bits n to n+k-1 from x.
unsigned scrunch_1(unsigned x, int n, int k) {
  unsigned hi_bits = 0xffu << n;
  return (x & ~hi_bits) | ((x >> k) & hi_bits);
}

// Remove bits n to n+k-1 from each byte in the buffer,
// then pack right. Return number of packed bytes.
size_t scrunch(uint8_t *buf, size_t size, int n, int k) {
  size_t i_src = 0, i_dst = 0;
  unsigned src_bits = 0; // Scrunched source bit buffer.
  int n_src_bits = 0;    // Initially it's empty.
  for (;;) {
    // Get scrunched bits until the buffer has at least 8.
    while (n_src_bits < 8) {
      if (i_src >= size) { // Done when source bytes exhausted.
        // If there are left-over bits, add one more byte to output.
        if (n_src_bits > 0) buf[i_dst++] = src_bits;
        return i_dst;
      }
      // Pack 'em in.
      src_bits |= scrunch_1(buf[i_src++], n, k) << n_src_bits;
      n_src_bits += 8 - k;
    }
    // Write the lower 8 bits of the buffer to the destination byte.
    buf[i_dst++] = src_bits;
    src_bits >>= 8;
    n_src_bits -= 8;
  }
}

int main(void) {
  uint8_t x[] = { 0xaa, 0xaa, 0xaa, 0xaa };
  size_t n = scrunch(x, 4, 2, 3);
  for (size_t i = 0; i < n; i++) {
    printf("%x ", x[i]);
  }
  printf("\n");
  return 0;
}