11

I have a byte array generated by a random number generator. I want to put this into the STL bitset.

Unfortunately, it looks like Bitset only supports the following constructors:

  1. A string of 1's and 0's like "10101011"
  2. An unsigned long. (my byte array will be longer)

The only solution I can think of now is to read the byte array bit by bit and make a string of 1's and 0's. Does anyone have a more efficient solution?

6 Answers 6

11

Something like this?

#include <bitset>
#include <climits>

template<size_t numBytes>
std::bitset<numBytes * CHAR_BIT> bytesToBitset(uint8_t *data)
{
    std::bitset<numBytes * CHAR_BIT> b;

    for(int i = 0; i < numBytes; ++i)
    {
        uint8_t cur = data[i];
        int offset = i * CHAR_BIT;

        for(int bit = 0; bit < CHAR_BIT; ++bit)
        {
            b[offset] = cur & 1;
            ++offset;   // Move to next bit in b
            cur >>= 1;  // Move to next bit in array
        }
    }

    return b;
}

And an example usage:

int main()
{
    std::array<uint8_t, 4> bytes = { 0xDE, 0xAD, 0xBE, 0xEF };
    auto bits = bytesToBitset<bytes.size()>(bytes.data());
    std::cout << bits << std::endl;
}
Sign up to request clarification or add additional context in comments.

1 Comment

It's both faster and easier to set bits CHAR_BIT bits at a time coliru.stacked-crooked.com/a/2ada0153b6d6baf0
3

There's a 3rd constructor for bitset<> - it takes no parameters and sets all the bits to 0. I think you'll need to use that then walk through the array calling set() for each bit in the byte array that's a 1.

A bit brute-force, but it'll work. There will be a bit of complexity to convert the byte-index and bit offset within each byte to a bitset index, but it's nothing a little bit of thought (and maybe a run through under the debugger) won't solve. I think it's most likely simpler and more efficient than trying to run the array through a string conversion or a stream.

Comments

3

I have spent a lot of time by writing a reverse function (bitset -> byte/char array). There it is:

    bitset<SIZE> data = ...

    // bitset to char array
    char current = 0;
    int offset = 0;
    for (int i = 0; i < SIZE; ++i) {
        if (data[i]) { // if bit is true
            current |= (char)(int)pow(2, i - offset * CHAR_BIT); // set that bit to true in current masked value
        } // otherwise let it to be false
        if ((i + 1) % CHAR_BIT == 0) { // every 8 bits
            buf[offset++] = current; // save masked value to buffer & raise offset of buffer
            current = 0; // clear masked value
        }
    }
    
    // now we have the result in "buf" (final size of contents in buffer is "offset")

Comments

2

Well, let's be honest, I was bored and started to think there had to be a slightly faster way than setting each bit.

template<int numBytes>
std::bitset<numBytes * CHARBIT bytesToBitset(byte *data)
{
    std::bitset<numBytes * CHAR_BIT> b = *data;

    for(int i = 1; i < numBytes; ++i)
    {
        b <<= CHAR_BIT;  // Move to next bit in array
        b |= data[i];    // Set the lowest CHAR_BIT bits
    }

    return b;
}

This is indeed slightly faster, at least as long as the byte array is smaller than 30 elements (depending on your optimization-flags passed to compiler). Larger array than that and the time used by shifting the bitset makes setting each bit faster.

Comments

2

Here is my implementation using template meta-programming.
Loops are done in the compile-time.
I took @strager version, modified it in order to prepare for TMP:

  • changed order of iteration (so that I could make recursion from it);
  • reduced number of used variables.

Modified version with loops in a run-time:

template <size_t nOfBytes>
void bytesToBitsetRunTimeOptimized(uint8_t* arr, std::bitset<nOfBytes * CHAR_BIT>& result) {
  for(int i = nOfBytes - 1; i >= 0; --i) {
    for(int bit = 0; bit < CHAR_BIT; ++bit) {
      result[i * CHAR_BIT + bit] = ((arr[i] >> bit) & 1);
    }
  }
}

TMP version based on it:

template<size_t nOfBytes, int I, int BIT> struct LoopOnBIT {
  static inline void bytesToBitset(uint8_t* arr, std::bitset<nOfBytes * CHAR_BIT>& result) {
    result[I * CHAR_BIT + BIT] = ((arr[I] >> BIT) & 1);
    LoopOnBIT<nOfBytes, I, BIT+1>::bytesToBitset(arr, result);
  }
};
// stop case for LoopOnBIT
template<size_t nOfBytes, int I> struct LoopOnBIT<nOfBytes, I, CHAR_BIT> {
  static inline void bytesToBitset(uint8_t* arr, std::bitset<nOfBytes * CHAR_BIT>& result) { }
};

template<size_t nOfBytes, int I> struct LoopOnI {
  static inline void bytesToBitset(uint8_t* arr, std::bitset<nOfBytes * CHAR_BIT>& result) {
    LoopOnBIT<nOfBytes, I, 0>::bytesToBitset(arr, result);
    LoopOnI<nOfBytes, I-1>::bytesToBitset(arr, result);
  }
};
// stop case for LoopOnI
template<size_t nOfBytes> struct LoopOnI<nOfBytes, -1> {
  static inline void bytesToBitset(uint8_t* arr, std::bitset<nOfBytes * CHAR_BIT>& result) { }
};

template <size_t nOfBytes>
void bytesToBitset(uint8_t* arr, std::bitset<nOfBytes * CHAR_BIT>& result) {
  LoopOnI<nOfBytes, nOfBytes - 1>::bytesToBitset(arr, result);
}

client code:

uint8_t arr[]={0x6A};
  std::bitset<8> b; 
  bytesToBitset<1>(arr,b);

Comments

0

you can initialize the bitset from a stream. I can't remember how to wrangle a byte[] into a stream, but...

from http://www.sgi.com/tech/stl/bitset.html

  bitset<12> x;

  cout << "Enter a 12-bit bitset in binary: " << flush;
  if (cin >> x) {
    cout << "x =        " << x << endl;
    cout << "As ulong:  " << x.to_ulong() << endl;
    cout << "And with mask: " << (x & mask) << endl;
    cout << "Or with mask:  " << (x | mask) << 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.