ravenspoint
ravenspoint

Reputation: 20616

Access bits in memory

I want to assemble a message bit by bit, then handle the message as a vector of unsigned characters ( e.g. to calculate the CRC )

I can assemble the message OK, using either a std::vector<bool> or a std::bitset

I can copy the assembled message to a std::vector doing it bit by bit. ( Note: the meesage is padded so that its length is an integer number of bytes )

// assemble message
std::vector<bool> bitMessage;
...

// copy the bits one by one into bytes and add them to the message
std::vector<unsigned char> myMessage;

// loop over bytes
for (int kbyte = 0;
     kbyte < bitMessage.size() / 8;
     kbyte++)
{
    unsigned char byte = 0;

    // loop over bits
    for (int kbit = 0;
         kbit < 8;
         kbit++)
    {
        // add bit to byte
        byte += bitMessage[8 * kbyte + kbit] << kbit;
    }

    // add byte to message
    myMessage.push_back(byte);
}

This works.

But it seems awfully slow! I would like to use std::memcpy.

For a 'normal' vector I would do

memcpy(
  myMessage.data(),
  bitMessage.data(),
  bitMessage.size() / 8 );

or

memcpy(
  &myMessage[0],
  &bitMessage[0],
  bitMessage.size() / 8 );

but neither of these methods is possible with either a vector<bool> or bitset

Question: Is there a way to get a pointer to the memory where the bits are stored?


The answer is: not with std::vector<bool> or std::bitset

However, with some hints , especially from @Ayxan Haqverdili, it is possible to write a small class that will accept single bits and construct a well mannered std::vector<unsigned char> as we go along.

/** Build a message bit by bit, creating an unsigned character vector of integer length
 * 
 * Hides the messy bit twiddling required,
 * allowing bits to be added to the end of the message
 * 
 * The message is automatically padded at the end with zeroes
 */
class cTwiddle
{
public:
    std::vector<unsigned char> myMessage;

    cTwiddle() : myBitLength(0) {}

    /** add a bit to end of message
     * @param[in] bit 
     */
    void add(bool bit)
    {
        // check if message vector is full
        if (!(myBitLength % 8))
        {
            // add byte to end of message
            myMessage.push_back(0);
        }
        
        // control order bits are added to a byte
        int shift = 7 - (myBitLength % 8);  // add bits from left to right ( MSB first )
        // int shift = (myBitLength % 8);  // add bits from right to left ( LSB first )

        myMessage.back() += (1 & bit) << shift;
        myBitLength++;
    }

private:
    int myBitLength;
};

Upvotes: 3

Views: 215

Answers (2)

Aykhan Hagverdili
Aykhan Hagverdili

Reputation: 30005

Apparently neither of those classes define the layout. Just write your own class and define the layout you want:

template <int size>
class BitSet final {
 private:
  unsigned char buffer[size / 8 + (size % 8 != 0)] = {};

 public:
  constexpr bool get(size_t index) const noexcept {
    return (buffer[index / 8] >> (index % 8)) & 1U;
  }

  constexpr void set(size_t index) noexcept {
    buffer[index / 8] |= (1U << (index % 8));
  }

  constexpr void clear(size_t index) noexcept {
    buffer[index / 8] &= ~(1U << (index % 8));
  }
};

Memcpy-ing this class is perfectly fine. Otherwise, you might also provide direct access to the byte array.

Alternatively, you can dynamically allocate the buffer:

#include <memory>

class DynBitSet final {
 private:
  size_t size = 0;
  std::unique_ptr<unsigned char[]> buffer;

 public:
  explicit DynBitSet(size_t bitsize)
      : size(bitsize / 8 + (bitsize % 8 != 0)),
        buffer(new unsigned char[size]{}) {}

  bool get(size_t index) const noexcept {
    return (buffer[index / 8] >> (index % 8)) & 1U;
  }

  void set(size_t index) noexcept { buffer[index / 8] |= (1U << (index % 8)); }

  void clear(size_t index) noexcept {
    buffer[index / 8] &= ~(1U << (index % 8));
  }

  auto bitSize() const noexcept { return size * 8; }
  auto byteSize() const noexcept { return size; }
  auto const* byteBuffer() const noexcept { return buffer.get(); }
};

Upvotes: 3

KamilCuk
KamilCuk

Reputation: 142005

Is there a way to get a pointer to the memory where the bits are stored [in std::vector]?

No. The idea is that it should be not possible.

Is there a way

Fun fact: In glibc the member in iterator is public.

#include <vector>
#include <iostream>
int main() {
    std::vector<bool> vec{1,0,1,0,1,1,1,1};
    std::cout << *vec.begin()._M_p << '\n';
}

Upvotes: 0

Related Questions