Last active
December 19, 2015 16:29
-
-
Save mrk21/5984540 to your computer and use it in GitHub Desktop.
C++でパケットの解析とかで必要になったが、x86だとリトルエンディアンになるので標準のビットフィールドは使えない…のでテンプレートクラスとして作ってみた。 (g++ 4.8, c++1y) => ライブラリにした https://github.com/mrk21/bitfield
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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