Please take a review of my simple bit manipulator:
#ifndef BIT_STREAM_H
#define BIT_STREAM_H
#include <cstdint>
class BitStream {
public:
explicit BitStream(uint8_t* buffer, size_t buffer_size) : m_Buffer(buffer), m_BufferSize(buffer_size) {
}
virtual ~BitStream() = default;
public:
inline uint64_t Mask(uint64_t offset, size_t size) const {
return ((1 << size) - 1) << offset;
}
public:
inline size_t GetBufferSize() const {
return m_BufferSize;
}
inline size_t GetOffset() const {
return m_Offset;
}
inline bool GetErrorState() const {
return m_ErrorState;
}
protected:
uint8_t* m_Buffer = nullptr;
size_t m_BufferSize = 0;
size_t m_Offset = 0;
bool m_ErrorState = false;
};
class BitReader : public BitStream {
public:
explicit BitReader(uint8_t* buffer, size_t buffer_size) : BitStream(buffer, buffer_size) {
}
template <size_t buffer_size>
explicit BitReader(uint8_t(&buffer)[buffer_size]) : BitStream(buffer, buffer_size) {
}
public:
template<const size_t bit_size, typename value_t = uint64_t>
inline value_t Read(value_t df = 0) {
const size_t bpb = 8;
const size_t byte_offset = m_Offset / bpb;
const size_t relative_offset = m_Offset - (byte_offset * bpb);
if (m_ErrorState) {
return df;
}
if (((m_Offset + bit_size) / bpb) >= m_BufferSize) {
m_ErrorState = true;
return df;
}
m_Offset += bit_size;
return (*(value_t*)&m_Buffer[byte_offset] & Mask(relative_offset, bit_size)) >> relative_offset;
}
};
class BitWriter : public BitStream {
public:
explicit BitWriter(uint8_t* buffer, size_t buffer_size) : BitStream(buffer, buffer_size) {
}
template <size_t buffer_size>
explicit BitWriter(uint8_t(&buffer)[buffer_size]) : BitStream(buffer, buffer_size) {
}
public:
template<const size_t bit_size, typename value_t = uint64_t>
inline void Write(value_t value) {
const size_t bpb = 8;
const size_t byte_offset = m_Offset / bpb;
const size_t relative_offset = m_Offset - (byte_offset * bpb);
if (m_ErrorState) {
return;
}
if (((m_Offset + bit_size) / bpb) >= m_BufferSize) {
m_ErrorState = true;
return;
}
m_Offset += bit_size;
*(value_t*)&m_Buffer[byte_offset] = (*(value_t*)&m_Buffer[byte_offset] & ~Mask(relative_offset, bit_size)) | (value << relative_offset);
}
};
#endif // BIT_STREAM_H
Example of usage:
#include "BitStream.h"
#include <iostream>
#include <cassert>
int main() {
uint8_t buffer[2];
memset(buffer, 0, sizeof(buffer));
BitWriter writer(buffer);
writer.Write<4>(15);
writer.Write<2>(0);
writer.Write<6>(63);
assert(!writer.GetErrorState());
BitReader reader(buffer);
std::cout << reader.Read<4>() << std::endl;
std::cout << reader.Read<2>() << std::endl;
std::cout << reader.Read<6>() << std::endl;
assert(!reader.GetErrorState());
std::cin.ignore();
}
Later in plan add exceptions, to string converter method.