Загрузка данных


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