use crate::blocks::{ChannelBlock, DataType};
pub use crate::types::DecodedValue;
const CN_FLAG_ALL_INVALID: u32 = 0x01; const CN_FLAG_INVAL_BIT_VALID: u32 = 0x02;
#[derive(Debug, Clone, PartialEq)]
pub struct DecodedChannelValue {
pub value: DecodedValue,
pub is_valid: bool,
}
pub fn check_value_validity(
record: &[u8],
record_id_size: usize,
cg_data_bytes: u32,
channel: &ChannelBlock,
) -> bool {
if channel.flags & CN_FLAG_ALL_INVALID != 0 {
return false;
}
if channel.flags & (CN_FLAG_ALL_INVALID | CN_FLAG_INVAL_BIT_VALID) == 0 {
return true;
}
let inval_byte_offset =
record_id_size + cg_data_bytes as usize + (channel.pos_invalidation_bit >> 3) as usize;
let inval_bit_index = (channel.pos_invalidation_bit & 0x07) as usize;
if inval_byte_offset < record.len() {
let inval_byte = record[inval_byte_offset];
let bit_is_set = (inval_byte >> inval_bit_index) & 0x01 != 0;
!bit_is_set
} else {
true
}
}
pub fn decode_channel_value(
record: &[u8],
record_id_size: usize,
channel: &ChannelBlock,
) -> Option<DecodedValue> {
decode_value_internal(record, record_id_size, channel)
}
pub fn decode_channel_value_with_validity(
record: &[u8],
record_id_size: usize,
cg_data_bytes: u32,
channel: &ChannelBlock,
) -> Option<DecodedChannelValue> {
let value = decode_value_internal(record, record_id_size, channel)?;
let is_valid = check_value_validity(record, record_id_size, cg_data_bytes, channel);
Some(DecodedChannelValue { value, is_valid })
}
fn decode_value_internal(
record: &[u8],
record_id_size: usize,
channel: &ChannelBlock,
) -> Option<DecodedValue> {
let base_offset = record_id_size + channel.byte_offset as usize;
let bit_offset = channel.bit_offset as usize;
let bit_count = channel.bit_count as usize;
let slice: &[u8] = if channel.channel_type == 1 && channel.data_addr != 0 {
record
} else {
let num_bytes = if matches!(
channel.data_type,
DataType::StringLatin1
| DataType::StringUtf8
| DataType::StringUtf16LE
| DataType::StringUtf16BE
| DataType::ByteArray
| DataType::MimeSample
| DataType::MimeStream
) {
bit_count / 8
} else {
(bit_offset + bit_count).div_ceil(8).max(1)
};
if base_offset + num_bytes > record.len() {
return None;
}
&record[base_offset..base_offset + num_bytes]
};
match &channel.data_type {
DataType::UnsignedIntegerLE => {
let raw = slice
.iter()
.rev()
.fold(0u64, |acc, &b| (acc << 8) | b as u64);
let shifted = raw >> bit_offset;
let mask = if bit_count >= 64 {
u64::MAX
} else {
(1u64 << bit_count) - 1
};
Some(DecodedValue::UnsignedInteger(shifted & mask))
}
DataType::UnsignedIntegerBE => {
let raw = slice.iter().fold(0u64, |acc, &b| (acc << 8) | b as u64);
let shifted = raw >> bit_offset;
let mask = if bit_count >= 64 {
u64::MAX
} else {
(1u64 << bit_count) - 1
};
Some(DecodedValue::UnsignedInteger(shifted & mask))
}
DataType::SignedIntegerLE => {
let raw = slice
.iter()
.rev()
.fold(0u64, |acc, &b| (acc << 8) | b as u64);
let shifted = raw >> bit_offset;
let mask = if bit_count >= 64 {
u64::MAX
} else {
(1u64 << bit_count) - 1
};
let unsigned = shifted & mask;
let sign_bit = 1u64 << (bit_count - 1);
let signed = if unsigned & sign_bit != 0 {
(unsigned as i64) | (!(mask as i64))
} else {
unsigned as i64
};
Some(DecodedValue::SignedInteger(signed))
}
DataType::SignedIntegerBE => {
let raw = slice.iter().fold(0u64, |acc, &b| (acc << 8) | b as u64);
let shifted = raw >> bit_offset;
let mask = if bit_count >= 64 {
u64::MAX
} else {
(1u64 << bit_count) - 1
};
let unsigned = shifted & mask;
let sign_bit = 1u64 << (bit_count - 1);
let signed = if unsigned & sign_bit != 0 {
(unsigned as i64) | (!(mask as i64))
} else {
unsigned as i64
};
Some(DecodedValue::SignedInteger(signed))
}
DataType::FloatLE => {
let raw = slice
.iter()
.rev()
.fold(0u64, |acc, &b| (acc << 8) | b as u64);
if bit_count == 32 {
Some(DecodedValue::Float(f32::from_bits(raw as u32) as f64))
} else if bit_count == 64 {
Some(DecodedValue::Float(f64::from_bits(raw)))
} else {
None
}
}
DataType::FloatBE => {
let raw = slice.iter().fold(0u64, |acc, &b| (acc << 8) | b as u64);
if bit_count == 32 {
Some(DecodedValue::Float(f32::from_bits(raw as u32) as f64))
} else if bit_count == 64 {
Some(DecodedValue::Float(f64::from_bits(raw)))
} else {
None
}
}
DataType::StringLatin1 => {
let s: String = slice.iter().map(|&b| b as char).collect();
Some(DecodedValue::String(s.trim_end_matches('\0').to_string()))
}
DataType::StringUtf8 => match std::str::from_utf8(slice) {
Ok(s) => Some(DecodedValue::String(s.trim_end_matches('\0').to_string())),
Err(_) => Some(DecodedValue::String(String::from("<Invalid UTF8>"))),
},
DataType::StringUtf16LE => {
if !slice.len().is_multiple_of(2) {
return None;
}
let u16_data: Vec<u16> = slice
.chunks_exact(2)
.map(|chunk| u16::from_le_bytes(chunk.try_into().unwrap()))
.collect();
match String::from_utf16(&u16_data) {
Ok(s) => Some(DecodedValue::String(s.trim_end_matches('\0').to_string())),
Err(_) => Some(DecodedValue::String(String::from("<Invalid UTF16LE>"))),
}
}
DataType::StringUtf16BE => {
if !slice.len().is_multiple_of(2) {
return None;
}
let u16_data: Vec<u16> = slice
.chunks_exact(2)
.map(|chunk| u16::from_be_bytes(chunk.try_into().unwrap()))
.collect();
match String::from_utf16(&u16_data) {
Ok(s) => Some(DecodedValue::String(s.trim_end_matches('\0').to_string())),
Err(_) => Some(DecodedValue::String(String::from("<Invalid UTF16BE>"))),
}
}
DataType::ByteArray => Some(DecodedValue::ByteArray(slice.to_vec())),
DataType::MimeSample => Some(DecodedValue::MimeSample(slice.to_vec())),
DataType::MimeStream => Some(DecodedValue::MimeStream(slice.to_vec())),
_ => Some(DecodedValue::Unknown),
}
}