在 C++ 中连接位
Concatenating Bits in C++
我正在开发一个用于卷积编码器和比特打孔的软件。
所以在我的程序中,每次循环都会生成一个 55 位长度的数据。它存储在 unsigned long long 类型变量的前 55 位中。每次迭代后,我必须将这 55 位传输到 unsigned char 缓冲区(缓冲区相当大,大约 500 字节)。这些位必须连续存储在该缓冲区中。 (即没有间隙)。是否有一些小技巧可以轻松地连接起来?
编辑:有人正确地向我指出,因为我通过 uint64_t*
为字符缓冲区设置别名,我违反了严格的别名规则,并且也容易受到攻击由于字节顺序不同而导致的错误。然而,如果您对自己的平台有一定的保证,这可能仍然有效。它可以通过使用 64 位元素的缓冲区而不是单个字节来解决。
如果您想要一个不依赖于任何外部库的准系统算法,您可以使用下面的代码片段。请注意,bitset 仅用于显示结果并确保其有效。
作为测试,我定义了一个 55 位模式,由 54 个连续的 1 组成,后跟一个 0。64 位值 (x
) 中的其余 9 位也是零。缓冲区是一个 500 字节的字符数组,我将其别名为 auint64_t*
。该算法跟踪当前的 64 位块 (currentBlock
) 和该块中的当前位 (currentBit
)。它执行以下操作:
- 移动位模式,使其从当前位位置开始。
- OR 当前 64 位块的结果。这意味着位模式的第一部分连接到当前块的剩余部分。例如,在第二次迭代中,第一个块的前55位被填充时,剩余的9位将被占用。
- 更新
currentBit
变量。
检查 currentBlock
是否溢出,如果溢出则移动到下一个块并连接 55 位模式的其余部分。
#include <iostream>
#include <bitset> // only used for testing, not for the algorithm
using namespace std;
int main()
{
size_t nBits = 55;
// 54 1's, followed by 10 0's
uint64_t x = 0b1111111111111111111111111111111111111111111111111111110000000000;
// 500 byte buffer:
char buf8[500]{};
uint64_t *buf = reinterpret_cast<uint64_t*>(&buf8[0]);
// This would be better; use this if possible
// uint64_t buf[100];
int currentBit = 0;
int currentBlock = 0;
// concatenate the 55-bitpattern 10 times
for (int i = 0; i != 10; ++i)
{
buf[currentBlock] |= (x >> currentBit);
currentBit += nBits;
if (currentBit >= 64)
{
++currentBlock;
currentBit %= 64;
buf[currentBlock] |= x << (nBits - currentBit);
}
}
// TEST
for (int i = 0; i != 5; ++i)
cout << bitset<64>(buf[i]) << '\n';
}
您或许应该将其概括化并将其封装在一个函数中。这取决于你。该程序按原样产生以下输出:
1111111111111111111111111111111111111111111111111111110111111111
1111111111111111111111111111111111111111111110111111111111111111
1111111111111111111111111111111111110111111111111111111111111111
1111111111111111111111111110111111111111111111111111111111111111
1111111111111111110111111111111111111111111111111111111111111111
注意每 55 位标记 0。
我不久前在需要处理位时写了这个class。它也可能对您有用。这是:
#include <deque>
#include <vector>
#include <algorithm>
class bitStream {
public:
bitStream() {}
/// Copies the data from another bitStream into this one upon construction
bitStream(const bitStream& bStream, bool reverse=false) {
this->appendData(bStream, reverse);
}
/// Copies the data from a vector of booleans upon construction
bitStream(const vector<bool>& vec, bool reverse=false) {
this->appendData(vec, reverse);
}
/// Appends data to the stream from a uint64_t type. The lower-n bits will be appended, starting with the highest bit of those by default.
void appendData(uint64_t data, size_t n, bool reverse=false) {
deque<bool> _buffer;
n = (n>64)?64:n;
for (int i=0; i<n; i++) {
_oneBit tmp;
tmp.data = data;
_buffer.push_back(tmp.data);
data >>= 0x1;
}
if (!reverse) std::reverse(_buffer.begin(), _buffer.end());
for (const auto v: _buffer) _data.push_back(v);
}
/// Appends data to the stream from a C-style array of booleans
void appendData(bool* data, size_t n, bool reverse=false) {
if (reverse) {
for (int i=0; i<n; i++) this->appendBit(*(data+(n-i-1)));
} else {
for (int i=0; i<n; i++) this->appendBit(*(data+i));
}
}
/// Appends data to the stream from a vector of booleans
void appendData(const vector<bool>& vec, bool reverse=false) {
if (reverse) {
for (auto i = vec.size()-1; vec.size() > i; --i) this->appendBit(vec.at(i));
} else {
for (const auto& v : vec) this->appendBit(v);
}
}
/// Appends a single bit
void appendBit(bool bit) {
_data.push_back(bit);
}
/// Appends the bits from another bitStream object to this one
void appendData(const bitStream& bStream, bool reverse=false) {
if (!bStream.getSize()) return;
if (reverse) {
for (int i=0; i<bStream.getSize(); i++) this->appendBit(*(bStream.getData()+(bStream.getSize()-i-1)));
} else {
for (int i=0; i<bStream.getSize(); i++) this->appendBit(*(bStream.getData()+i));
}
}
/// Returns a pointer to the begining of the data (read-only!)
const bool* getData() const { return &_data.front(); }
/// Reference to the bit at a specified position (assignable, but at lest n+1 elements must exist before calling!)
bool& operator[] (size_t n) {
if (n>_data.size()-1) throw runtime_error("Out of range!");
return _data.at(n);
}
/// Fills your vector with the data chopped up into "sizeof(T)"-byte pieces.
template <typename T>
void getDataAsVector(vector<T>& vec) {
vec.clear();
size_t oSize = sizeof(T)*8;
T tmp = 0x0;
for (int i=0; i<_data.size(); i++) {
if (!(i%oSize) && i) {
vec.push_back(tmp);
tmp = 0x0;
}
tmp <<= 0x1;
tmp |= _data[i];
}
vec.push_back(tmp);
}
/// Returns the number of bits that are stored
size_t getSize() const { return _data.size(); }
/// Reverses the stored bits
void reverse() { std::reverse(_data.begin(), _data.end()); }
private:
deque<bool> _data;
struct _oneBit {
uint8_t data:1;
};
};
使用起来相当简单,所以我没有费心写一个例子。
首先,我从来没有真正在项目中使用过它,所以那里可能有一些错误(我记得只是非常简单地测试过),而且它还没有完全完成(有几个可能我还没有实现的构造函数)。此外,尽管名称为 "stream",但它与 C++ 中的流无关。
所以基本上它可以让你存储位。您放入其中的位将存储在连续的内存地址中(是的,这是每个内存字节一位,但无论如何),这些地址在对象的生命周期内保持不变(这是因为 deque
不'重新分配自己不像 vector
)!您可以以各种方式(从不同的数据源,以相反或非相反的顺序)向它附加位,并以不同的形式读回它们。在您的情况下, appendData(uint64_t data, size_t n, bool reverse=false)
函数是您可以用来填充它的函数。要取回您的数据,您可以 vector<char>
填充被切成 1 字节片段(字符)的数据,如果您愿意的话!
再次强调,请不要将此视为 100% 测试和工作的东西。我只是简单地测试了它,所以你应该尝试一下,看看它是否适合你!我想我会与您分享,以防您觉得它有帮助。
我正在开发一个用于卷积编码器和比特打孔的软件。
所以在我的程序中,每次循环都会生成一个 55 位长度的数据。它存储在 unsigned long long 类型变量的前 55 位中。每次迭代后,我必须将这 55 位传输到 unsigned char 缓冲区(缓冲区相当大,大约 500 字节)。这些位必须连续存储在该缓冲区中。 (即没有间隙)。是否有一些小技巧可以轻松地连接起来?
编辑:有人正确地向我指出,因为我通过 uint64_t*
为字符缓冲区设置别名,我违反了严格的别名规则,并且也容易受到攻击由于字节顺序不同而导致的错误。然而,如果您对自己的平台有一定的保证,这可能仍然有效。它可以通过使用 64 位元素的缓冲区而不是单个字节来解决。
如果您想要一个不依赖于任何外部库的准系统算法,您可以使用下面的代码片段。请注意,bitset 仅用于显示结果并确保其有效。
作为测试,我定义了一个 55 位模式,由 54 个连续的 1 组成,后跟一个 0。64 位值 (x
) 中的其余 9 位也是零。缓冲区是一个 500 字节的字符数组,我将其别名为 auint64_t*
。该算法跟踪当前的 64 位块 (currentBlock
) 和该块中的当前位 (currentBit
)。它执行以下操作:
- 移动位模式,使其从当前位位置开始。
- OR 当前 64 位块的结果。这意味着位模式的第一部分连接到当前块的剩余部分。例如,在第二次迭代中,第一个块的前55位被填充时,剩余的9位将被占用。
- 更新
currentBit
变量。 检查
currentBlock
是否溢出,如果溢出则移动到下一个块并连接 55 位模式的其余部分。#include <iostream> #include <bitset> // only used for testing, not for the algorithm using namespace std; int main() { size_t nBits = 55; // 54 1's, followed by 10 0's uint64_t x = 0b1111111111111111111111111111111111111111111111111111110000000000; // 500 byte buffer: char buf8[500]{}; uint64_t *buf = reinterpret_cast<uint64_t*>(&buf8[0]); // This would be better; use this if possible // uint64_t buf[100]; int currentBit = 0; int currentBlock = 0; // concatenate the 55-bitpattern 10 times for (int i = 0; i != 10; ++i) { buf[currentBlock] |= (x >> currentBit); currentBit += nBits; if (currentBit >= 64) { ++currentBlock; currentBit %= 64; buf[currentBlock] |= x << (nBits - currentBit); } } // TEST for (int i = 0; i != 5; ++i) cout << bitset<64>(buf[i]) << '\n'; }
您或许应该将其概括化并将其封装在一个函数中。这取决于你。该程序按原样产生以下输出:
1111111111111111111111111111111111111111111111111111110111111111
1111111111111111111111111111111111111111111110111111111111111111
1111111111111111111111111111111111110111111111111111111111111111
1111111111111111111111111110111111111111111111111111111111111111
1111111111111111110111111111111111111111111111111111111111111111
注意每 55 位标记 0。
我不久前在需要处理位时写了这个class。它也可能对您有用。这是:
#include <deque>
#include <vector>
#include <algorithm>
class bitStream {
public:
bitStream() {}
/// Copies the data from another bitStream into this one upon construction
bitStream(const bitStream& bStream, bool reverse=false) {
this->appendData(bStream, reverse);
}
/// Copies the data from a vector of booleans upon construction
bitStream(const vector<bool>& vec, bool reverse=false) {
this->appendData(vec, reverse);
}
/// Appends data to the stream from a uint64_t type. The lower-n bits will be appended, starting with the highest bit of those by default.
void appendData(uint64_t data, size_t n, bool reverse=false) {
deque<bool> _buffer;
n = (n>64)?64:n;
for (int i=0; i<n; i++) {
_oneBit tmp;
tmp.data = data;
_buffer.push_back(tmp.data);
data >>= 0x1;
}
if (!reverse) std::reverse(_buffer.begin(), _buffer.end());
for (const auto v: _buffer) _data.push_back(v);
}
/// Appends data to the stream from a C-style array of booleans
void appendData(bool* data, size_t n, bool reverse=false) {
if (reverse) {
for (int i=0; i<n; i++) this->appendBit(*(data+(n-i-1)));
} else {
for (int i=0; i<n; i++) this->appendBit(*(data+i));
}
}
/// Appends data to the stream from a vector of booleans
void appendData(const vector<bool>& vec, bool reverse=false) {
if (reverse) {
for (auto i = vec.size()-1; vec.size() > i; --i) this->appendBit(vec.at(i));
} else {
for (const auto& v : vec) this->appendBit(v);
}
}
/// Appends a single bit
void appendBit(bool bit) {
_data.push_back(bit);
}
/// Appends the bits from another bitStream object to this one
void appendData(const bitStream& bStream, bool reverse=false) {
if (!bStream.getSize()) return;
if (reverse) {
for (int i=0; i<bStream.getSize(); i++) this->appendBit(*(bStream.getData()+(bStream.getSize()-i-1)));
} else {
for (int i=0; i<bStream.getSize(); i++) this->appendBit(*(bStream.getData()+i));
}
}
/// Returns a pointer to the begining of the data (read-only!)
const bool* getData() const { return &_data.front(); }
/// Reference to the bit at a specified position (assignable, but at lest n+1 elements must exist before calling!)
bool& operator[] (size_t n) {
if (n>_data.size()-1) throw runtime_error("Out of range!");
return _data.at(n);
}
/// Fills your vector with the data chopped up into "sizeof(T)"-byte pieces.
template <typename T>
void getDataAsVector(vector<T>& vec) {
vec.clear();
size_t oSize = sizeof(T)*8;
T tmp = 0x0;
for (int i=0; i<_data.size(); i++) {
if (!(i%oSize) && i) {
vec.push_back(tmp);
tmp = 0x0;
}
tmp <<= 0x1;
tmp |= _data[i];
}
vec.push_back(tmp);
}
/// Returns the number of bits that are stored
size_t getSize() const { return _data.size(); }
/// Reverses the stored bits
void reverse() { std::reverse(_data.begin(), _data.end()); }
private:
deque<bool> _data;
struct _oneBit {
uint8_t data:1;
};
};
使用起来相当简单,所以我没有费心写一个例子。
首先,我从来没有真正在项目中使用过它,所以那里可能有一些错误(我记得只是非常简单地测试过),而且它还没有完全完成(有几个可能我还没有实现的构造函数)。此外,尽管名称为 "stream",但它与 C++ 中的流无关。
所以基本上它可以让你存储位。您放入其中的位将存储在连续的内存地址中(是的,这是每个内存字节一位,但无论如何),这些地址在对象的生命周期内保持不变(这是因为 deque
不'重新分配自己不像 vector
)!您可以以各种方式(从不同的数据源,以相反或非相反的顺序)向它附加位,并以不同的形式读回它们。在您的情况下, appendData(uint64_t data, size_t n, bool reverse=false)
函数是您可以用来填充它的函数。要取回您的数据,您可以 vector<char>
填充被切成 1 字节片段(字符)的数据,如果您愿意的话!
再次强调,请不要将此视为 100% 测试和工作的东西。我只是简单地测试了它,所以你应该尝试一下,看看它是否适合你!我想我会与您分享,以防您觉得它有帮助。