Skip to content

Instantly share code, notes, and snippets.

@mrk21
Last active December 19, 2015 16:29
Show Gist options
  • Save mrk21/5984540 to your computer and use it in GitHub Desktop.
Save mrk21/5984540 to your computer and use it in GitHub Desktop.
C++でパケットの解析とかで必要になったが、x86だとリトルエンディアンになるので標準のビットフィールドは使えない…のでテンプレートクラスとして作ってみた。 (g++ 4.8, c++1y) => ライブラリにした https://github.com/mrk21/bitfield
#include <iostream>
#include <array>
#include <algorithm>
#include <boost/format.hpp>
namespace bf {
constexpr uint32_t mask(std::size_t bit) {
return bit < 32 ? (1 << bit)-1 : -1;
}
constexpr uint32_t ceil(double value) {
return (value - static_cast<uint32_t>(value)) > 0.0001 ? value + 1 : value;
}
template<typename T>
uint32_t multibyte_value(std::size_t byte, const T * addrss) {
auto * base = reinterpret_cast<const uint8_t *>(addrss);
uint32_t value = 0x00;
byte &= mask(byte*8);
for (std::size_t i=0; i < byte; ++i) {
value |= base[i] << ((byte-i-1)*8);
}
return value;
}
template<std::size_t N>
struct byte_container: public std::array<uint8_t, N> {
template<typename T> constexpr static const byte_container & from(const T * address ) { return *reinterpret_cast<const byte_container *>(address); }
template<typename T> constexpr static byte_container & from( T * address ) { return *reinterpret_cast< byte_container *>(address); }
template<typename T> constexpr static const byte_container & from(const T & reference) { return from(&reference); }
template<typename T> constexpr static byte_container & from( T & reference) { return from(&reference); }
uint32_t value(void) const {
return multibyte_value(this->size(), this->data());
}
operator uint32_t(void) const {
return this->value();
}
friend std::ostream & operator<<(std::ostream & os, const byte_container & container) {
std::for_each(container.begin(), container.end(), [&os](uint32_t v){
os << boost::format("%02X ") % v;
});
return os;
}
};
template<std::size_t Size, std::size_t Offset = 0>
struct bitfield {
constexpr static auto SIZE = Size;
constexpr static auto OFFSET = Offset;
constexpr static auto NEXT_OFFSET = OFFSET + SIZE;
constexpr static auto START_POSITION = OFFSET / 8;
constexpr static auto START_PADDING = OFFSET % 8;
constexpr static auto END_POSITION = ceil(NEXT_OFFSET / 8.0);
constexpr static auto END_PADDING = END_POSITION*8 - NEXT_OFFSET;
constexpr static auto CONTAINER_SIZE = END_POSITION - START_POSITION;
constexpr static auto INCLUSION_CONTAINER_SIZE = END_POSITION;
constexpr static auto MAX_VALUE = mask(SIZE);
constexpr static auto CONTAINER_MASK = MAX_VALUE << END_PADDING;
using container_type = byte_container<CONTAINER_SIZE>;
using inclusion_container_type = byte_container<INCLUSION_CONTAINER_SIZE>;
template<std::size_t NextSize>
using next_bitfield = bitfield<NextSize, NEXT_OFFSET>;
container_type & container(void) { return container_type::from(this + START_POSITION); }
const container_type & container(void) const { return container_type::from(this + START_POSITION); }
uint32_t get(void) const {
return (this->container().value() & CONTAINER_MASK) >> END_PADDING;
}
bitfield & set(uint32_t value) {
value = (value << END_PADDING) & CONTAINER_MASK;
auto clear_mask = ~CONTAINER_MASK;
auto & container = this->container();
auto v_it = container_type::from(value).rbegin();
auto m_it = container_type::from(clear_mask).rbegin();
for (auto it = container.begin(); it != container.end(); ++it) {
*it &= *m_it++;
*it |= *v_it++;
}
return *this;
}
operator uint32_t(void) const {
return this->get();
}
bitfield & operator=(uint32_t value) {
return this->set(value);
}
friend std::ostream & operator<<(std::ostream & os, const bitfield & v){
return os << v.get();
}
};
}
struct bitfield_test {
using version_type = bf::bitfield<8>;
using flag_1_type = version_type::next_bitfield<1>;
using flag_2_type = flag_1_type::next_bitfield<1>;
using value_1_type = flag_2_type::next_bitfield<12>;
using value_2_type = value_1_type::next_bitfield<2>;
using container_type = value_2_type::inclusion_container_type;
union {
container_type data;
version_type version;
flag_1_type flag_1;
flag_2_type flag_2;
value_1_type value_1;
value_2_type value_2;
};
friend std::ostream & operator<<(std::ostream & out, const bitfield_test & bf){
out << "data: " << bf.data << std::endl;
out << "version: " << boost::format("%1$d (0x%1$X)") % bf.version << std::endl;
out << "flag_1: " << boost::format("%1$d (0x%1$X)") % bf.flag_1 << std::endl;
out << "flag_2: " << boost::format("%1$d (0x%1$X)") % bf.flag_2 << std::endl;
out << "value_1: " << boost::format("%1$d (0x%1$X)") % bf.value_1 << std::endl;
out << "value_2: " << boost::format("%1$d (0x%1$X)") % bf.value_2 << std::endl;
return out;
};
};
int main(void) {
bitfield_test target;
target.data[0] = 0x10;
target.data[1] = 0xA0;
target.data[2] = 0x01;
// data: 10 A0 01
// version: 16 (0x10)
// flag_1: 1 (0x1)
// flag_2: 0 (0x0)
// value_1: 2048 (0x800)
// value_2: 1 (0x1)
std::cout << target << std::endl;
// data: 10 E0 01
// version: 16 (0x10)
// flag_1: 1 (0x1)
// flag_2: 1 (0x1)
// value_1: 2048 (0x800)
// value_2: 1 (0x1)
target.flag_2 = 1;
std::cout << target << std::endl;
// data: 10 C1 F5
// version: 16 (0x10)
// flag_1: 1 (0x1)
// flag_2: 1 (0x1)
// value_1: 125 (0x7D)
// value_2: 1 (0x1)
target.value_1 = 125;
std::cout << target << std::endl;
// data: 10 C1 F6
// version: 16 (0x10)
// flag_1: 1 (0x1)
// flag_2: 1 (0x1)
// value_1: 125 (0x7D)
// value_2: 2 (0x2)
target.value_2 = 2;
std::cout << target << std::endl;
// data: 10 C1 F5
// version: 16 (0x10)
// flag_1: 1 (0x1)
// flag_2: 1 (0x1)
// value_1: 125 (0x7D)
// value_2: 1 (0x1)
target.value_2 = 13;
std::cout << target << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment