namespace
{
template<
std::size_t DestinationBit, // Индекс текущего бита в целевом массиве
std::size_t RemainingBits, // Сколько бит осталось записать из числа
std::size_t SourceBit, // Индекс текущего бита в исходном числе
std::size_t Size
>
inline void pack_bits_impl(
uint8_t (&data)[Size],
uint64_t value
) noexcept
{
constexpr std::size_t byte = DestinationBit / 8;
constexpr std::size_t offset = DestinationBit % 8;
// Сколько бит физически помещается в текущий байт за эту итерацию
constexpr std::size_t chunk_bits =
RemainingBits < (8 - offset)
? RemainingBits
: (8 - offset);
// Маска для вырезания битов из исходного числа
constexpr uint8_t value_mask =
static_cast<uint8_t>((1U << chunk_bits) - 1U);
// Вырезаем биты из исходного числа и сдвигаем их на позицию в целевом байте
const uint8_t part = static_cast<uint8_t>(
((value >> SourceBit) & value_mask) << offset
);
// Если пишем байт целиком с нулевого бита - затираем старые данные, иначе накладываем через OR
if constexpr (offset == 0 && chunk_bits == 8) {
data[byte] = part;
} else {
data[byte] |= part;
}
// Рекурсивный переход к следующему байту, если биты ещё остались
if constexpr (RemainingBits > chunk_bits) {
pack_bits_impl<
DestinationBit + chunk_bits,
RemainingBits - chunk_bits,
SourceBit + chunk_bits
>(data, value);
}
}
} // namespace
template<
std::size_t StartBit,
std::size_t BitCount,
std::size_t Size,
typename T
>
inline void pack_bits(
uint8_t (&data)[Size],
T value
) noexcept
{
static_assert(std::is_integral_v<T>);
static_assert(BitCount > 0);
static_assert(StartBit + BitCount <= Size * 8);
uint64_t packed_value;
// Безопасное приведение любых знаковых/беззнаковых типов к uint64_t
if constexpr (std::is_same_v<std::remove_cv_t<T>, bool>) {
packed_value = value ? 1U : 0U;
} else if constexpr (std::is_signed_v<T>) {
packed_value =
static_cast<uint64_t>(
static_cast<int64_t>(value)
);
} else {
packed_value = static_cast<uint64_t>(value);
}
pack_bits_impl<
StartBit,
BitCount,
0
>(data, packed_value);
}
uint8_t data[8];
bool failure_supply = false;
bool failure_recircular = true;
bool failure_indoor = false;
int16_t outdoor = 255;
func old() {
pack_bits<0, 1>(data, failure_supply);
pack_bits<1, 1>(data, failure_recircular);
pack_bits<4, 1>(data, failure_indoor);
pack_bits<16, 16>(data, outdoor);
}
func new() {
data[0] = failure_supply |
(failure_recircular << 1) |
(failure_indoor << 4);
memcpy(data[2], outdoor, sizeof(int16_t);
}