以7位访问8位数据

问题描述 投票:4回答:7

我有一个100个uint8_t的数组,它被视为一个800位的流,并且一次处理7位。换句话说,如果8位数组的第一个元素保持0b11001100而第二个元素保持ob11110000,那么当我以7位格式读取它时,7位数组的第一个元素将是0b1100110而第二个元素是0b0111100将是struct uint7_t { uint8_t i1:7; }; union uint7_8_t { uint8_t u8[100]; uint7_t u7[115]; }; ,其余的2位在第3位被保留。我尝试的第一件事就是工会......

xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx

但当然一切都是字节对齐的,我最终只是放弃了每个元素的第8位。

有没有人对我如何做到这一点有任何想法?

为了清楚起见,这是联合结果的直观表示:

0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx 32位8位数据 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 32位7位数据。

这代表了我想做的事情:

xxxxxxx xxxxxxx xxxxxxx xxxxxxx xxxx 32位8位数据 uint7_t x{5}; // simple value Arr<uint7_t> arr(10); // array of size 10 arr[0] = x; // set element uint7_t y = arr[0]; // get element arr.push_back(uint7_t{9}); // add element arr.push_back(x); // std::cout << "Array size is " << arr.size() << '\n'; // get size for(auto&& i : arr) std::cout << i << '\n'; // range-for to read values int z{50}; for(auto&& i : arr) i = z++; // range-for to change values auto&& v = arr[1]; // get reference to second element v = 99; // change second element via reference 32位7位数据。

我知道最后的位可能被填充但是没关系,我只想要一次访问每个字节7位而不丢失任何800位。到目前为止,我能想到的唯一方法就是大量转移,这当然会起作用,但我确信有一种更清洁的方式(?)

提前感谢您的任何答案。

c++ arrays unions 8bit 7bit
7个回答
2
投票

不确定你的意思是“清洁工”。通常,处理此类问题的人经常考虑将移位和屏蔽作为正确的原始工具来使用。人们可以做一些事情,比如使用一种从流中读取任意数量的比特的方法来定义比特流抽象。这种抽象有时会出现在压缩应用程序中。该方法的内部当然确实使用移位和掩蔽。

一种相当干净的方法是编写一个函数,在无符号字符数组中的任何位索引处提取7位数。使用除法将位索引转换为字节索引,使用模数来获取字节内的位索引。然后转移并掩盖。输入位可以跨越两个字节,因此您必须在提取之前将16位值粘合在一起,或者执行两次较小的提取和/或它们来构造结果。

如果我的目标是适度的表现,我可能采取以下两种方法之一:

第一个有两个状态变量,表示从当前和下一个字节中取出多少位。它将使用移位,屏蔽和按位或产生当前输出(例如,作为int的0到127之间的数字),然后循环将通过添加和模数更新两个状态变量,并将增加当前字节指针如果消耗了第一个字节中的所有位。

第二种方法是将64位(输出值为8的输出)加载到64位整数中,并使用完全展开的结构来提取8个输出中的每一个。在不使用未对齐的内存读取的情况下执行此操作需要构造64位整数零碎。 (56位是特殊的,因为起始位位置是字节对齐的。)

为了快速实现,我可能会尝试在Halide中编写SIMD代码。我认为这超出了范围。 (并不清楚它实际上会赢得多少。)

一次读取多个字节为整数的设计可能需要考虑处理器字节排序。


1
投票

这是一个使用向量bool特化的解决方案。它还使用类似的机制允许通过引用对象访问七位元素。

成员函数允许以下操作:

#include <vector>
#include <iterator>
#include <iostream>

struct uint7_t {
    unsigned int i : 7;
};

struct seven_bit_ref {
    size_t begin;
    size_t end;
    std::vector<bool>& bits;

    seven_bit_ref& operator=(const uint7_t& right)
    {
        auto it{bits.begin()+begin};
        for(int mask{1}; mask != 1 << 7; mask <<= 1)
            *it++ = right.i & mask;
        return *this;
    }

    operator uint7_t() const
    {
        uint7_t r{};
        auto it{bits.begin() + begin};
        for(int i{}; i < 7; ++i)
            r.i += *it++ << i;
        return r;
    }

    seven_bit_ref operator*()
    {
        return *this;
    }

    void operator++()
    {
        begin += 7;
        end += 7;
    }

    bool operator!=(const seven_bit_ref& right)
    {
        return !(begin == right.begin && end == right.end);
    }

    seven_bit_ref operator=(int val)
    {
        uint7_t temp{};
        temp.i = val;
        operator=(temp);
        return *this;
    }

};

template<typename T>
class Arr;

template<>
class Arr<uint7_t> {
public:
    Arr(size_t size) : bits(size * 7, false) {}

    seven_bit_ref operator[](size_t index)
    {
        return {index * 7, index * 7 + 7, bits};
    }
    size_t size()
    {
        return bits.size() / 7;
    }
    void push_back(uint7_t val)
    {
        for(int mask{1}; mask != 1 << 7; mask <<= 1){
            bits.push_back(val.i & mask);
        }
    }

    seven_bit_ref begin()
    {
        return {0, 7, bits};
    }

    seven_bit_ref end()
    {
        return {size() * 7, size() * 7 + 7, bits};
    }

    std::vector<bool> bits;
};

std::ostream& operator<<(std::ostream& os, uint7_t val)
{
    os << val.i;
    return os;
}

int main()
{
    uint7_t x{5};               // simple value
    Arr<uint7_t> arr(10);       // array of size 10
    arr[0] = x;                 // set element
    uint7_t y = arr[0];         // get element
    arr.push_back(uint7_t{9});  // add element
    arr.push_back(x);           //
    std::cout << "Array size is " 
        << arr.size() << '\n';  // get size
    for(auto&& i : arr) 
        std::cout << i << '\n'; // range-for to read values
    int z{50};
    for(auto&& i : arr)
        i = z++;                // range-for to change values
    auto&& v = arr[1];          // get reference
    v = 99;                     // change via reference
    std::cout << "\nAfter changes:\n";
    for(auto&& i : arr)
        std::cout << i << '\n';
}

完整计划:

// convert 8 x 7bit ints in one go
void extract8(const uint8_t input[7], uint8_t output[8])
{
  output[0] =   input[0] & 0x7F;
  output[1] =  (input[0] >> 7)  | ((input[1] << 1) & 0x7F);
  output[2] =  (input[1] >> 6)  | ((input[2] << 2) & 0x7F);
  output[3] =  (input[2] >> 5)  | ((input[3] << 3) & 0x7F);
  output[4] =  (input[3] >> 4)  | ((input[4] << 4) & 0x7F);
  output[5] =  (input[4] >> 3)  | ((input[5] << 5) & 0x7F);
  output[6] =  (input[5] >> 2)  | ((input[6] << 6) & 0x7F);
  output[7] =   input[6] >> 1;
}

// convert array of 7bit ints to 8bit
void seven_bit_to_8bit(const uint8_t* const input, uint8_t* const output, const size_t count)
{
  size_t count8 = count >> 3;
  for(size_t i = 0; i < count8; ++i)
  {
    extract8(input + 7 * i, output + 8 * i);
  }

  // handle remaining (upto) 7 bytes 
  const size_t countr = (count % 8);
  if(countr)
  {
    // how many bytes do we need to copy from the input?
    size_t remaining_bits = 7 * countr;
    if(remaining_bits % 8)
    {
      // round to next nearest multiple of 8
      remaining_bits += (8 - remaining_bits % 8);
    }
    remaining_bits /= 8;
    {
      uint8_t in[7] = {0}, out[8] = {0};
      for(size_t i = 0; i < remaining_bits; ++i)
      {
        in[i] = input[count8 * 7 + i];
      }
      extract8(in, out);
      for(size_t i = 0; i < countr; ++i)
      {
        output[count8 * 8 + i] = in[i];
      }
    }
  }
}

1
投票

以8个为一组处理它们(因为8x7很好地舍入到8位对齐的东西)。按位运算符是这里的一天的顺序。与最后(最多)7个数字相关是有点太过于冒险,但并非不可能。 (此代码假设这些是无符号的7位整数!如果位[6]为1,则有符号转换需要考虑翻转最高位)

live example on ideone

1
投票

以下代码按照您的要求工作,但首先输出和Before changing values...: 7 bit representation: 1111111 0000000 0000000 0000000 0000000 0000000 0000000 0000000 8 bit representation: 11111110 00000000 00000000 00000000 00000000 00000000 00000000 After changing values...: 7 bit representation: 1000000 1001100 1110010 1011010 1010100 0000111 1111110 0000000 8 bit representation: 10000001 00110011 10010101 10101010 10000001 11111111 00000000 8 Bits: 11111111 to ulong: 255 7 Bits: 1111110 to ulong: 126 After changing values...: 7 bit representation: 0010000 0101010 0100000 0000000 0000000 0000000 0000000 0000000 8 bit representation: 00100000 10101001 00000000 00000000 00000000 00000000 00000000

输出:

std::bitset

在一个名为BitVector的类中使用selIdx非常直接。我实现了一个getter和setter。 getter还返回给定索引M的std :: bitset,给定的模板参数大小为M。给定的idx将乘以给定的大小selIdx以获得正确的位置。返回的bitset也可以转换为数字或字符串值。 setter使用uint8_t值作为输入,再使用索引M。这些位将被移位到位集中的正确位置。

此外,由于模板参数#include <iostream> #include <bitset> template <size_t N> class BitVector { private: std::bitset<N> _data; public: BitVector (unsigned long num) : _data (num) { }; BitVector (const std::string& str) : _data (str) { }; template <size_t M> std::bitset<M> getBits (size_t selIdx) { std::bitset<M> retBitset; for (size_t idx = 0; idx < M; ++idx) { retBitset |= (_data[M * selIdx + idx] << (M - 1 - idx)); } return retBitset; } template <size_t M> void setBits (size_t selIdx, uint8_t num) { const unsigned char* curByte = reinterpret_cast<const unsigned char*> (&num); for (size_t bitIdx = 0; bitIdx < 8; ++bitIdx) { bool bitSet = (1 == ((*curByte & (1 << (8 - 1 - bitIdx))) >> (8 - 1 - bitIdx))); _data.set(M * selIdx + bitIdx, bitSet); } } void print_7_8() { std:: cout << "\n7 bit representation: "; for (size_t idx = 0; idx < (N / 7); ++idx) { std::cout << getBits<7>(idx) << " "; } std:: cout << "\n8 bit representation: "; for (size_t idx = 0; idx < N / 8; ++idx) { std::cout << getBits<8>(idx) << " "; } } }; int main () { BitVector<56> num = 127; std::cout << "Before changing values...:"; num.print_7_8(); num.setBits<8>(0, 0x81); num.setBits<8>(1, 0b00110011); num.setBits<8>(2, 0b10010101); num.setBits<8>(3, 0xAA); num.setBits<8>(4, 0x81); num.setBits<8>(5, 0xFF); num.setBits<8>(6, 0x00); std::cout << "\n\nAfter changing values...:"; num.print_7_8(); std::cout << "\n\n8 Bits: " << num.getBits<8>(5) << " to ulong: " << num.getBits<8>(5).to_ulong(); std::cout << "\n7 Bits: " << num.getBits<7>(6) << " to ulong: " << num.getBits<7>(6).to_ulong(); num = BitVector<56>(std::string("1001010100000100")); std::cout << "\n\nAfter changing values...:"; num.print_7_8(); return 0; } ,您可以使用不同大小的getter和setter,这意味着您可以使用7位或8位表示,但也可以使用3或任何您喜欢的方式。

我确信这段代码不是最好的速度,但我认为这是一个非常清晰和干净的解决方案。它也完全不完整,因为只有一个吸气剂,一个定位器和两个构造器。请记住实现有关索引和大小的错误检查。

码:

int bytes = 0x01234567;
bitset<32> bs(bytes);
cout << "Input: " << bs << endl;
for(int i = 0; i < 5; i++)
{
    bitset<7> slice(bs.to_string().substr(i*7, 7));
    cout << slice << endl;
}

0
投票

这是一种没有手动换档的方法。这只是一个粗略的POC,但希望你能从中得到一些东西。我不知道你是否能够轻松地将输入转换为bitset,但我认为它应该是可能的。

in

这也许比位移版本的性能要差得多,所以我不推荐它用于繁重的提升。


0
投票

您可以使用它来从int get7(const uint8_t *in, int index) { int fidx = index*7; int idx = fidx>>3; int sidx = fidx&7; return (in[idx]>>sidx|in[idx+1]<<(8-sidx))&0x7f; } 获取索引的第7位元素(请注意,它没有正确的数组处理结束)。简单,快速。

TurboPFor:Integer Compression

0
投票

您可以像在// Direct read access // b : bit width 0-16 (7 in your case) #define bzhi32(u,b) ((u) & ((1u <<(b))-1)) static inline unsigned bitgetx16(unsigned char *in, unsigned idx, unsigned b) { unsigned bidx = b*idx; return bzhi32( *(unsigned *)((uint16_t *)in+(bidx>>4)) >> (bidx& 0xf), b ); } 中一样使用直接访问或批量位打包/解包

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.