#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
use crate::error::FormatError;
use crate::message_type::MessageType;
const V3_FLAGS_DEFAULT: u8 = 0x0a;
const V3_FLAG_DEFINED: u8 = 0x20;
pub(crate) fn fill_value_message_v3(fill: Option<&[u8]>) -> Vec<u8> {
match fill {
None => vec![3, V3_FLAGS_DEFAULT],
Some(bytes) => {
let mut msg = Vec::with_capacity(6 + bytes.len());
msg.push(3); msg.push(V3_FLAGS_DEFAULT | V3_FLAG_DEFINED);
#[expect(
clippy::cast_possible_truncation,
reason = "a scalar fill value is at most a few bytes; its length fits u32"
)]
let len = bytes.len() as u32;
msg.extend_from_slice(&len.to_le_bytes());
msg.extend_from_slice(bytes);
msg
}
}
}
pub(crate) fn parse_defined_fill_value(
msg_type: MessageType,
data: &[u8],
) -> Result<Option<Vec<u8>>, FormatError> {
match msg_type {
MessageType::FillValueOld => {
let size = read_u32(data, 0)? as usize;
if size == 0 {
return Ok(None);
}
Ok(Some(read_bytes(data, 4, size)?))
}
MessageType::FillValue => {
let version = *data.first().ok_or(eof(1, data.len()))?;
match version {
1 | 2 => {
let defined = *data.get(3).ok_or(eof(4, data.len()))?;
if version == 2 && defined == 0 {
return Ok(None);
}
let size = read_u32(data, 4)? as usize;
if size == 0 {
return Ok(None);
}
if version == 1 && defined == 0 {
return Ok(None);
}
Ok(Some(read_bytes(data, 8, size)?))
}
3 => {
let flags = *data.get(1).ok_or(eof(2, data.len()))?;
if flags & V3_FLAG_DEFINED == 0 {
return Ok(None);
}
let size = read_u32(data, 2)? as usize;
if size == 0 {
return Ok(None);
}
Ok(Some(read_bytes(data, 6, size)?))
}
other => Err(FormatError::UnsupportedFillValueVersion(other)),
}
}
_ => Ok(None),
}
}
fn read_u32(data: &[u8], offset: usize) -> Result<u32, FormatError> {
let end = offset + 4;
let slice = data.get(offset..end).ok_or(eof(end, data.len()))?;
Ok(u32::from_le_bytes([slice[0], slice[1], slice[2], slice[3]]))
}
fn read_bytes(data: &[u8], offset: usize, len: usize) -> Result<Vec<u8>, FormatError> {
let end = offset.checked_add(len).ok_or(eof(usize::MAX, data.len()))?;
let slice = data.get(offset..end).ok_or(eof(end, data.len()))?;
Ok(slice.to_vec())
}
fn eof(expected: usize, available: usize) -> FormatError {
FormatError::UnexpectedEof {
expected,
available,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_message_is_unchanged() {
assert_eq!(fill_value_message_v3(None), vec![3, 0x0a]);
}
#[test]
fn defined_message_matches_reference_library_bytes() {
let msg = fill_value_message_v3(Some(&(-7i32).to_le_bytes()));
assert_eq!(msg, vec![3, 0x2a, 4, 0, 0, 0, 0xf9, 0xff, 0xff, 0xff]);
}
#[test]
fn v3_defined_round_trips_through_the_parser() {
let value = 3.5f64.to_le_bytes();
let msg = fill_value_message_v3(Some(&value));
let got = parse_defined_fill_value(MessageType::FillValue, &msg).unwrap();
assert_eq!(got.as_deref(), Some(&value[..]));
}
#[test]
fn v3_default_parses_as_no_value() {
let msg = fill_value_message_v3(None);
assert_eq!(
parse_defined_fill_value(MessageType::FillValue, &msg).unwrap(),
None
);
}
#[test]
fn v3_explicitly_undefined_parses_as_no_value() {
let msg = [3u8, 0x1a];
assert_eq!(
parse_defined_fill_value(MessageType::FillValue, &msg).unwrap(),
None
);
}
#[test]
fn v2_defined_parses_the_value() {
let msg = [2u8, 2, 2, 1, 4, 0, 0, 0, 0xf9, 0xff, 0xff, 0xff];
let got = parse_defined_fill_value(MessageType::FillValue, &msg).unwrap();
assert_eq!(got.as_deref(), Some(&[0xf9, 0xff, 0xff, 0xff][..]));
}
#[test]
fn v2_undefined_parses_as_no_value() {
let msg = [2u8, 2, 2, 0];
assert_eq!(
parse_defined_fill_value(MessageType::FillValue, &msg).unwrap(),
None
);
}
#[test]
fn v1_defined_parses_the_value() {
let msg = [1u8, 2, 2, 1, 2, 0, 0, 0, 0xed, 0xfe];
let got = parse_defined_fill_value(MessageType::FillValue, &msg).unwrap();
assert_eq!(got.as_deref(), Some(&[0xed, 0xfe][..]));
}
#[test]
fn v1_marked_undefined_ignores_stored_bytes() {
let msg = [1u8, 2, 2, 0, 4, 0, 0, 0, 1, 2, 3, 4];
assert_eq!(
parse_defined_fill_value(MessageType::FillValue, &msg).unwrap(),
None
);
}
#[test]
fn old_message_parses_the_value() {
let msg = [4u8, 0, 0, 0, 10, 20, 30, 40];
let got = parse_defined_fill_value(MessageType::FillValueOld, &msg).unwrap();
assert_eq!(got.as_deref(), Some(&[10, 20, 30, 40][..]));
}
#[test]
fn old_message_zero_size_is_no_value() {
let msg = [0u8, 0, 0, 0];
assert_eq!(
parse_defined_fill_value(MessageType::FillValueOld, &msg).unwrap(),
None
);
}
#[test]
fn truncated_size_field_errors() {
let msg = [3u8, 0x2a, 4, 0];
assert!(matches!(
parse_defined_fill_value(MessageType::FillValue, &msg),
Err(FormatError::UnexpectedEof { .. })
));
}
#[test]
fn truncated_value_field_errors() {
let msg = [3u8, 0x2a, 8, 0, 0, 0, 0xaa, 0xbb];
assert!(matches!(
parse_defined_fill_value(MessageType::FillValue, &msg),
Err(FormatError::UnexpectedEof { .. })
));
}
#[test]
fn unknown_version_errors() {
let msg = [9u8, 0];
assert!(matches!(
parse_defined_fill_value(MessageType::FillValue, &msg),
Err(FormatError::UnsupportedFillValueVersion(9))
));
}
#[test]
fn empty_body_errors() {
assert!(matches!(
parse_defined_fill_value(MessageType::FillValue, &[]),
Err(FormatError::UnexpectedEof { .. })
));
}
}