I want to convert an array of bits (bool* bitArray) where the values are 1s and 0s into an array of bytes (unsigned char* byteArray) where the values at each index would be one byte. For ex, index 0~7 in bitArray would go into byteArray[1].
How would I go about doing this? Assuming that I already have an array of bits (but the amount would be subject to change based on the incoming data). I am not worried about having it divisible by 8 because I will just add padding at the end of the bitArray to make it divisible by 8.
CodePudding user response:
Just just use bit shifts or a lookup array and and combine numbers with 1 bit set each with bitwise or for 8 bits at a time:
int main() {
bool input[] = {
false, false, false, true, true, true, false, false, false,
false, false, false, true, true, true, false, false, false,
false, false, false, true, true, true, false, false, false,
false, false, false, true, true, true, false, false, false,
};
constexpr auto len = sizeof(input) / sizeof(*input);
constexpr size_t outLen = ((len % 8 == 0) ? 0 : 1) len / 8;
uint8_t out[outLen];
bool* inPos = input;
uint8_t* outPos = out;
size_t remaining = len;
// output bytes where there are all 8 bits available
for (; remaining >= 8; remaining -= 8, outPos)
{
uint8_t value = 0;
for (size_t i = 0; i != 8; i, inPos)
{
if (*inPos)
{
value |= (1 << (7 - i));
}
}
*outPos = value;
}
if (remaining != 0)
{
// output byte that requires padding
uint8_t value = 0;
for (size_t i = 0; i != remaining; i, inPos)
{
if (*inPos)
{
value |= (1 << (7 - i));
}
}
*outPos = value;
}
for (auto v : out)
{
std::cout << static_cast<int>(v) << '\n';
}
return 0;
}
The rhs of the |=
operator could also be replaced with a lookup in the following array, if you consider this simpler to understand:
constexpr uint8_t Bits[8]
{
0b1000'0000,
0b0100'0000,
0b0010'0000,
0b0001'0000,
0b0000'1000,
0b0000'0100,
0b0000'0010,
0b0000'0001,
};
...
value |= Bits[i];
...
CodePudding user response:
You should be using std::bitset
for an array of bools, or std::vector<bool>
if it's dynamically sized. And std::array
for the array or again std::vector
for dynamic size. I've only done static size below and conversion to and from.
Converting involves a lot of bit shifts and loops for something that should be a memcpy (on little endian or unsigned char types). The compiler output for -O2 is bad. -O3 removes the loop and to_array2 gets interesting. gcc nearly manages to optimize it, clang actually gets it down to movzx eax, word ptr [rdi]
: https://godbolt.org/z/4chb8o81e
#include <array>
#include <bitset>
#include <climits>
template <typename T, std::size_t len>
constexpr std::bitset<sizeof(T) * CHAR_BIT * len> from_array(const std::array<T, len> &arr) {
std::bitset<sizeof(T) * CHAR_BIT * len> res;
std::size_t pos = 0;
for (auto x : arr) {
for(std::size_t i = 0; i < sizeof(T) * CHAR_BIT; i) {
res[pos ] = x & 1;
x >>= 1;
}
}
return res;
}
template <typename T, std::size_t len>
constexpr std::array<T, (len sizeof(T) * CHAR_BIT - 1) / (sizeof(T) * CHAR_BIT)> to_array(const std::bitset<len> &bit) {
std::array<T, (len sizeof(T) * CHAR_BIT - 1) / (sizeof(T) * CHAR_BIT)> res;
T mask = 1;
T t = 0;
std::size_t pos = 0;
for (std::size_t i = 0; i < len; i) {
if (bit[i]) t |= mask;
mask <<= 1;
if (mask == 0) {
mask = 1;
res[pos ] = t;
t = 0;
}
}
if constexpr (len % (sizeof(T) * CHAR_BIT) != 0) {
res[pos] = t;
}
return res;
}
std::bitset<16> from_array2(const std::array<unsigned char, 2> &arr) {
return from_array(arr);
}
std::array<unsigned short, 1> to_array2(const std::bitset<16> &bits) {
return to_array<unsigned short>(bits);
}
#include <iostream>
int main() {
std::array<unsigned char, 2> arr{0, 255};
std::bitset bits = from_array(arr);
std::cout << bits << std::endl;
std::bitset<16> bits2{0x1234};
std::array<unsigned short, 1> arr2 = to_array<unsigned short>(bits2);
std::cout << std::hex << arr2[0] << std::endl;
}