#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
use core::num::NonZeroUsize;
use crate::error::FormatError;
use crate::message_type::MessageType;
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct FillPattern<'a> {
element: Option<&'a [u8]>,
unknown: bool,
}
impl<'a> FillPattern<'a> {
pub(crate) const ZERO: Self = Self {
element: None,
unknown: false,
};
pub(crate) const UNKNOWN: Self = Self {
element: None,
unknown: true,
};
pub(crate) fn new(bytes: Option<&'a [u8]>, elem_size: NonZeroUsize) -> Self {
match bytes {
Some(b) if b.len() == elem_size.get() && b.iter().any(|&x| x != 0) => Self {
element: Some(b),
unknown: false,
},
_ => Self::ZERO,
}
}
pub(crate) fn element(self) -> Result<Option<&'a [u8]>, FormatError> {
if self.unknown {
return Err(FormatError::UnreadableFillValue);
}
Ok(self.element)
}
pub(crate) fn buffer(self, len: usize) -> Result<Vec<u8>, FormatError> {
if self.unknown {
return Err(FormatError::UnreadableFillValue);
}
let mut buf = vec![0u8; len];
self.apply(&mut buf)?;
Ok(buf)
}
pub(crate) fn apply(self, buf: &mut [u8]) -> Result<(), FormatError> {
if self.unknown {
return Err(FormatError::UnreadableFillValue);
}
let Some(element) = self.element else {
return Ok(());
};
for slot in buf.chunks_mut(element.len()) {
slot.copy_from_slice(&element[..slot.len()]);
}
Ok(())
}
}
pub(crate) enum PaddingFill {
Zero,
Value(Vec<u8>),
Unknown,
}
impl PaddingFill {
pub(crate) fn from_message(msg_type: MessageType, body: &[u8]) -> Self {
match fill_value_is_written(msg_type, body) {
Ok(false) => Self::Zero,
Ok(true) => match parse_defined_fill_value(msg_type, body) {
Ok(Some(bytes)) => Self::Value(bytes),
Ok(None) => Self::Zero,
Err(_) => Self::Unknown,
},
Err(_) => Self::Unknown,
}
}
pub(crate) fn pattern(&self, elem_size: NonZeroUsize) -> FillPattern<'_> {
match self {
Self::Zero => FillPattern::ZERO,
Self::Value(bytes) => FillPattern::new(Some(bytes), elem_size),
Self::Unknown => FillPattern::UNKNOWN,
}
}
}
const V3_FLAGS_DEFAULT: u8 = 0x0a;
const V3_FLAG_DEFINED: u8 = 0x20;
const FILL_TIME_NEVER: u8 = 1;
pub(crate) fn fill_value_is_written(
msg_type: MessageType,
data: &[u8],
) -> Result<bool, FormatError> {
match msg_type {
MessageType::FillValueOld => Ok(true),
MessageType::FillValue => {
let version = *data.first().ok_or(eof(1, data.len()))?;
match version {
1 | 2 => Ok(*data.get(2).ok_or(eof(3, data.len()))? != FILL_TIME_NEVER),
3 => {
let flags = *data.get(1).ok_or(eof(2, data.len()))?;
Ok((flags >> 2) & 0b11 != FILL_TIME_NEVER)
}
other => Err(FormatError::UnsupportedFillValueVersion(other)),
}
}
_ => Ok(true),
}
}
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),
}
}
#[cfg(test)]
mod fill_pattern_tests {
#[test]
fn an_unreadable_message_is_unknown_not_zero() {
let elem = NonZeroUsize::new(4).unwrap();
let mut defined = vec![3u8, V3_FLAGS_DEFAULT | 0x20, 4, 0, 0, 0];
defined.extend_from_slice(&[7u8, 0, 0, 0]);
assert!(matches!(
PaddingFill::from_message(MessageType::FillValue, &defined),
PaddingFill::Value(ref b) if b == &[7u8, 0, 0, 0]
));
let default = vec![3u8, V3_FLAGS_DEFAULT];
assert!(matches!(
PaddingFill::from_message(MessageType::FillValue, &default),
PaddingFill::Zero
));
let mut unknown_version = defined.clone();
unknown_version[0] = 9;
assert!(matches!(
PaddingFill::from_message(MessageType::FillValue, &unknown_version),
PaddingFill::Unknown
));
assert!(
PaddingFill::from_message(MessageType::FillValue, &unknown_version)
.pattern(elem)
.apply(&mut [0u8; 4])
.is_err()
);
assert!(matches!(
PaddingFill::from_message(MessageType::FillValue, &defined[..3]),
PaddingFill::Unknown
));
}
use super::*;
use crate::convert::nz;
#[test]
fn a_defined_fill_tiles_across_the_buffer() {
let seven = 7.0f64.to_le_bytes();
let p = FillPattern::new(Some(&seven), nz(8));
assert_eq!(p.buffer(24).unwrap(), seven.repeat(3));
assert!(seven.iter().filter(|&&b| b == 0).count() >= 6);
}
#[test]
fn a_fill_value_of_the_wrong_width_is_dropped() {
for bytes in [vec![1u8], vec![1, 2, 3], vec![1; 9], Vec::new()] {
let p = FillPattern::new(Some(&bytes), nz(8));
assert_eq!(
p.buffer(16).unwrap(),
vec![0u8; 16],
"a {}-byte fill over an 8-byte element must not tile",
bytes.len()
);
}
}
#[test]
fn an_all_zero_fill_is_the_zero_pattern() {
assert_eq!(
FillPattern::new(Some(&[0u8; 8]), nz(8)).buffer(16).unwrap(),
vec![0u8; 16]
);
assert_eq!(
FillPattern::new(None, nz(8)).buffer(16).unwrap(),
vec![0u8; 16]
);
assert_eq!(FillPattern::ZERO.buffer(16).unwrap(), vec![0u8; 16]);
}
#[test]
fn single_byte_elements_and_empty_buffers() {
let p = FillPattern::new(Some(&[0xAB]), nz(1));
assert_eq!(p.buffer(5).unwrap(), vec![0xAB; 5]);
assert_eq!(p.buffer(0).unwrap(), Vec::<u8>::new());
}
#[test]
fn an_unknown_fill_refuses_to_materialize() {
assert!(matches!(
FillPattern::UNKNOWN.buffer(16),
Err(FormatError::UnreadableFillValue)
));
assert!(FillPattern::UNKNOWN.buffer(0).is_err());
assert!(FillPattern::ZERO.buffer(0).is_ok());
}
#[test]
fn the_write_time_is_read_from_every_message_version() {
let v3 = |flags: u8| vec![3u8, flags, 4, 0, 0, 0, 7, 0, 0, 0];
assert!(!fill_value_is_written(MessageType::FillValue, &v3(0x26)).unwrap());
assert!(fill_value_is_written(MessageType::FillValue, &v3(0x2a)).unwrap());
assert!(fill_value_is_written(MessageType::FillValue, &v3(0x22)).unwrap());
for version in [1u8, 2] {
let msg = |write_time: u8| vec![version, 2, write_time, 1, 4, 0, 0, 0, 7, 0, 0, 0];
assert!(!fill_value_is_written(MessageType::FillValue, &msg(1)).unwrap());
assert!(fill_value_is_written(MessageType::FillValue, &msg(0)).unwrap());
assert!(fill_value_is_written(MessageType::FillValue, &msg(2)).unwrap());
}
assert!(
fill_value_is_written(MessageType::FillValueOld, &[4, 0, 0, 0, 7, 0, 0, 0]).unwrap()
);
assert!(fill_value_is_written(MessageType::FillValue, &[]).is_err());
assert!(fill_value_is_written(MessageType::FillValue, &[3]).is_err());
assert!(fill_value_is_written(MessageType::FillValue, &[2, 0]).is_err());
assert!(fill_value_is_written(MessageType::FillValue, &[9, 0]).is_err());
}
}
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 { .. })
));
}
}