简体   繁体   English

从缓冲区中删除第n位,然后移动其余位

[英]Remove nth bit from buffer, and shift the rest

Giving a uint8_t buffer of x length, I am trying to come up with a function or a macro that can remove nth bit (or n to n+i), then left-shift the remaining bits. 给出一个x长度的uint8_t缓冲区,我试图找到一个可以删除第n位(或n到n + i)的函数或宏,然后左移剩余的位。

example #1: 示例#1:

for input 0b76543210 0b76543210 ... then output should be 0b76543217 0b654321 ... 输入0b76543210 0b76543210 ...然后输出应为0b76543217 0b654321 ...

example #2: if the input is: 示例#2:如果输入是:

uint8_t input[8] = {
    0b00110011,
    0b00110011,
    ...
};

the output without the first bit, should be 没有第一位的输出应该是

uint8_t output[8] = {
    0b00110010,
    0b01100100,
    ...
};

I have tried the following to remove the first bit, but it did not work for the second group of bits. 我已尝试以下方法删除第一位,但它不适用于第二组位。

/* 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;
}

位 Update #1 In my case, the input will be uint64_t number, then I will use memmov to shift it one place to the left. 更新#1在我的情况下,输入将是uint64_t数字,然后我将使用memmov将其向左移动一个位置。

Update #2 The solution can be in C/C++, assembly(x86-64) or inline assembly. 更新#2解决方案可以是C / C ++,汇编(x86-64)或内联汇编。

This is really 2 subproblems: remove bits from each byte and pack the results. 这实际上是2个子问题:从每个字节中删除位并打包结果。 This is the flow of the code below. 这是下面代码的流程。 I wouldn't use a macro for this. 我不会为此使用宏。 Too much going on. 太多了。 Just inline the function if you're worried about performance at that level. 如果您担心该级别的性能,请简单介绍该功能。

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

This writes b5 ad 60 , which by my reckoning is correct. 这写了b5 ad 60 ,据我推算这是正确的。 A few other test cases work as well. 其他一些测试用例也可以使用。

Oops I coded it the first time shifting the wrong way, but include that here in case it's useful to someone. 哎呀我第一次用错误的方式对它进行了编码,但是在这里包含它以防万一它对某人有用。

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

This writes d6 5a b . 这写d6 5a b A few other test cases work as well. 其他一些测试用例也可以使用。

Something similar to this should work: 类似的东西应该工作:

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;
  }
}

I just tested it on some basic cases so maybe something is not exactly correct but this should move you onto the right track. 我只是在一些基本情况下对它进行了测试,所以也许某些东西不完全正确,但这应该会让你走上正确的轨道。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM