Home > Software engineering >  Access bits in memory
Access bits in memory

Time:01-04

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

        myMessage.back()  = (1 & bit) << (myBitLength % 8);
        myBitLength  ;
    }

private:
    int myBitLength;
};

CodePudding user response:

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

CodePudding user response:

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';
}
  •  Tags:  
  • Related