1

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.

2
  • "in hex" describes a way to display numbers. I don't believe this question involves anything being displayed or in hex. Commented Jun 2, 2022 at 17:30
  • I'm really missing a bitcast from std::bitset to std:array and back. Commented Jun 2, 2022 at 18:15

2 Answers 2

2

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];
...

Sign up to request clarification or add additional context in comments.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.