///|
/// Extracts 1..32 bits, least significant bit first, including unaligned fields.
/// signed=true interprets the result as two's complement.
pub fn extract_bits(
bytes : Bytes,
bit_offset : Int,
bit_size : Int,
signed? : Bool = false,
) -> Result[Int64, Diagnostic] {
if bit_offset < 0 || bit_size < 1 || bit_size > 32 {
return Err(
diagnostic(0, "bit_range", "Offset must be nonnegative and width 1..32"),
)
}
let start_byte = bit_offset / 8
let start_bit = bit_offset % 8
let end_byte = start_byte + (start_bit + bit_size + 7) / 8
if end_byte > bytes.length() {
return Err(
diagnostic(
start_byte, "short_report", "Report does not contain the requested bits",
),
)
}
let mut value : Int64 = 0
for i = 0; i < bit_size; i = i + 1 {
let position = start_bit + i
let bit = (bytes[start_byte + position / 8].to_int() >> (position % 8)) & 1
value = value | (bit.to_int64() << i)
}
if signed && (value & (1L << (bit_size - 1))) != 0 {
Ok(value - (1L << bit_size))
} else {
Ok(value)
}
}
///|
/// Copies 1..65536 payload bits into LSB-first bytes; trailing high bits are zero.
pub fn extract_bytes(
bytes : Bytes,
bit_offset : Int,
bit_size : Int,
) -> Result[Bytes, Diagnostic] {
if bit_offset < 0 || bit_size < 1 || bit_size > 65536 {
return Err(
diagnostic(
0, "bit_range", "Offset must be nonnegative and width 1..65536",
),
)
}
let start_byte = bit_offset / 8
let start_bit = bit_offset % 8
let needed = (start_bit + bit_size + 7) / 8
if start_byte > bytes.length() || needed > bytes.length() - start_byte {
return Err(
diagnostic(
start_byte, "short_report", "Report does not contain the requested bits",
),
)
}
let packed : FixedArray[Byte] = FixedArray::make((bit_size + 7) / 8, 0)
for i = 0; i < bit_size; i = i + 1 {
let source = start_bit + i
let bit = (bytes[start_byte + source / 8].to_int() >> (source % 8)) & 1
packed[i / 8] = (packed[i / 8].to_int() | (bit << (i % 8))).to_byte()
}
Ok(Bytes::from_array(packed.exact_view()))
}