簡體   English   中英

以7位訪問8位數據

[英]Accessing 8-bit data as 7-bit

我有一個100 uint8_t的數組,它被視為一個800位的流,並且一次處理7位。 換句話說,如果8位數組的第一個元素保持0b11001100而第二個元素保持ob11110000那么當我以7位格式讀取它時,7位數組的第一個元素將是0b1100110和第二個元素將是0b0111100 ,其余2位保持在第3位。 我嘗試的第一件事就是工會......

struct uint7_t {
    uint8_t i1:7;
};

union uint7_8_t {
    uint8_t u8[100];
    uint7_t u7[115];
};

但當然一切都是字節對齊的,我最終只是放棄了每個元素的第8位。

有沒有人知道如何做到這一點?

為了清楚起見,這是聯合結果的直觀表示:

xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 32位8位數據
0xxxxxxx 0xxxxxxx 0xxxxxxx 0xxxxxxx 32位7位數據。

這代表了我想做的事情:

xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 32位8位數據
xxxxxxx xxxxxxx xxxxxxx xxxxxxx xxxx 32位7位數據。

我知道最后的位可能被填充但是沒關系,我只想要一次訪問每個字節7位而不丟失任何800位。 到目前為止,我能想到的唯一方法就是大量轉移,這當然會起作用,但我確信有一種更清潔的方式(?)

提前感謝您的任何答案。

不確定你的意思是“清潔工”。 通常,處理此類問題的人經常考慮將移位和屏蔽作為正確的原始工具來使用。 人們可以做一些事情,比如使用一種從流中讀取任意數量的比特的方法來定義比特流抽象。 這種抽象有時會出現在壓縮應用程序中。 該方法的內部當然確實使用移位和掩蔽。

一種相當干凈的方法是編寫一個函數,在無符號字符數組中的任何位索引處提取7位數。 使用除法將位索引轉換為字節索引,使用模數來獲取字節內的位索引。 然后轉移並掩蓋。 輸入位可以跨越兩個字節,因此您必須在提取之前將16位值粘合在一起,或者執行兩次較小的提取和/或它們來構造結果。

如果我的目標是適度的表現,我可能采取以下兩種方法之一:

第一個有兩個狀態變量,表示從當前和下一個字節中取出多少位。 它將使用移位,屏蔽和按位或產生當前輸出(例如,作為int的0到127之間的數字),然后循環將通過添加和模數更新兩個狀態變量,並將增加當前字節指針如果消耗了第一個字節中的所有位。

第二種方法是將64位(輸出值為8的輸出)加載到64位整數中,並使用完全展開的結構來提取8個輸出中的每一個。 在不使用未對齊的內存讀取的情況下執行此操作需要構造64位整數零碎。 (56位是特殊的,因為起始位位置是字節對齊的。)

為了快速實現,我可能會嘗試在Halide中編寫SIMD代碼。 我認為這超出了范圍。 (並不清楚它實際上會贏得多少。)

一次讀取多個字節為整數的設計可能需要考慮處理器字節排序。

這是一個使用向量bool特化的解決方案。 它還使用類似的機制允許通過引用對象訪問七位元素。

成員函數允許以下操作:

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

完整計划:

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

以8個為一組處理它們(因為8x7很好地舍入到8位對齊的東西)。 按位運算符是這里的一天的順序。 與最后(最多)7個數字相關是有點太過於冒險,但並非不可能。 (此代碼假設這些是無符號的7位整數!如果位[6]為1,則有符號轉換需要考慮翻轉最高位)

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

以下代碼按照您的要求工作,但首先是ideone上的輸出和實例

輸出:

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 

在名為BitVector的類中使用std :: bitset非常簡單。 我實現了一個getter和setter。 getter在給定的索引selIdx處返回一個std :: bitset,給定的模板參數大小為M 給定的idx將乘以給定的大小M以獲得正確的位置。 返回的bitset也可以轉換為數字或字符串值。
setter使用uint8_t值作為輸入,並再次使用索引selIdx 這些位將被移位到位集中的正確位置。

此外,由於模板參數M ,您可以使用具有不同大小的getter和setter,這意味着您可以使用7位或8位表示,但也可以使用3或任何您喜歡的。

我確信這段代碼不是最好的速度,但我認為這是一個非常清晰和干凈的解決方案。 它也完全不完整,因為只有一個吸氣劑,一個定位器和兩個構造器。 請記住實現有關索引和大小的錯誤檢查。

碼:

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

這是一種沒有手動換檔的方法。 這只是一個粗略的POC,但希望你能從中得到一些東西。 我不知道你是否能夠輕松地將輸入轉換為bitset,但我認為它應該是可能的。

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

這也許比位移版本的性能要差得多,所以我不推薦它用於繁重的提升。

您可以用它來從獲得index個7位元素in (注意,它不具有的陣列處理正確結束)。 簡單,快速。

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

您可以使用TurboPFor:Integer Compression中的直接訪問或批量位打包/解包

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM