#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::{string::String, string::ToString, vec, vec::Vec};
use crate::error::FormatError;
pub const FILTER_DEFLATE: u16 = 1;
pub const FILTER_SHUFFLE: u16 = 2;
pub const FILTER_FLETCHER32: u16 = 3;
pub const FILTER_SZIP: u16 = 4;
pub const FILTER_NBIT: u16 = 5;
pub const FILTER_SCALEOFFSET: u16 = 6;
#[derive(Debug, Clone, PartialEq)]
pub struct FilterDescription {
pub filter_id: u16,
pub name: Option<String>,
pub flags: u16,
pub client_data: Vec<u32>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FilterPipeline {
pub version: u8,
pub filters: Vec<FilterDescription>,
}
fn ensure_len(data: &[u8], offset: usize, needed: usize) -> Result<(), FormatError> {
match offset.checked_add(needed) {
Some(end) if end <= data.len() => Ok(()),
_ => Err(FormatError::UnexpectedEof {
expected: offset.saturating_add(needed),
available: data.len(),
}),
}
}
impl FilterPipeline {
pub fn parse(data: &[u8]) -> Result<FilterPipeline, FormatError> {
ensure_len(data, 0, 2)?;
let version = data[0];
let number_of_filters = data[1] as usize;
match version {
1 => Self::parse_v1(data, number_of_filters),
2 => Self::parse_v2(data, number_of_filters),
_ => Err(FormatError::InvalidFilterPipelineVersion(version)),
}
}
fn parse_v1(data: &[u8], number_of_filters: usize) -> Result<FilterPipeline, FormatError> {
ensure_len(data, 0, 8)?;
let mut pos = 8;
let mut filters = Vec::with_capacity(number_of_filters);
for _ in 0..number_of_filters {
ensure_len(data, pos, 8)?;
let filter_id = u16::from_le_bytes([data[pos], data[pos + 1]]);
let name_length = u16::from_le_bytes([data[pos + 2], data[pos + 3]]) as usize;
let flags = u16::from_le_bytes([data[pos + 4], data[pos + 5]]);
let num_client_data = u16::from_le_bytes([data[pos + 6], data[pos + 7]]) as usize;
pos += 8;
let name = if name_length > 0 {
ensure_len(data, pos, name_length)?;
let name_bytes = &data[pos..pos + name_length];
let name_str = core::str::from_utf8(
name_bytes.split(|&b| b == 0).next().unwrap_or(name_bytes),
)
.unwrap_or("")
.to_string();
let padded = (name_length + 7) & !7;
pos += padded;
Some(name_str)
} else {
None
};
ensure_len(data, pos, num_client_data * 4)?;
let mut client_data = Vec::with_capacity(num_client_data);
for _ in 0..num_client_data {
let val = u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
client_data.push(val);
pos += 4;
}
if !num_client_data.is_multiple_of(2) {
pos += 4;
}
filters.push(FilterDescription {
filter_id,
name,
flags,
client_data,
});
}
Ok(FilterPipeline { version: 1, filters })
}
fn parse_v2(data: &[u8], number_of_filters: usize) -> Result<FilterPipeline, FormatError> {
let mut pos = 2;
let mut filters = Vec::with_capacity(number_of_filters);
for _ in 0..number_of_filters {
ensure_len(data, pos, 2)?;
let filter_id = u16::from_le_bytes([data[pos], data[pos + 1]]);
pos += 2;
let name_length = if filter_id >= 256 {
ensure_len(data, pos, 2)?;
let nl = u16::from_le_bytes([data[pos], data[pos + 1]]) as usize;
pos += 2;
nl
} else {
0
};
ensure_len(data, pos, 4)?;
let flags = u16::from_le_bytes([data[pos], data[pos + 1]]);
let num_client_data = u16::from_le_bytes([data[pos + 2], data[pos + 3]]) as usize;
pos += 4;
let name = if name_length > 0 {
ensure_len(data, pos, name_length)?;
let name_bytes = &data[pos..pos + name_length];
let name_str = core::str::from_utf8(
name_bytes.split(|&b| b == 0).next().unwrap_or(name_bytes),
)
.unwrap_or("")
.to_string();
pos += name_length;
Some(name_str)
} else {
None
};
ensure_len(data, pos, num_client_data * 4)?;
let mut client_data = Vec::with_capacity(num_client_data);
for _ in 0..num_client_data {
let val = u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
client_data.push(val);
pos += 4;
}
filters.push(FilterDescription {
filter_id,
name,
flags,
client_data,
});
}
Ok(FilterPipeline { version: 2, filters })
}
pub fn serialize(&self) -> Vec<u8> {
match self.version {
1 => self.serialize_v1(),
2 => self.serialize_v2(),
_ => vec![self.version, 0],
}
}
fn serialize_v1(&self) -> Vec<u8> {
let mut buf = Vec::new();
buf.push(1); buf.push(self.filters.len() as u8);
buf.extend_from_slice(&[0u8; 6]);
for f in &self.filters {
buf.extend_from_slice(&f.filter_id.to_le_bytes());
let name_bytes = match &f.name {
Some(name) => {
let mut nb = name.as_bytes().to_vec();
nb.push(0); nb
}
None => Vec::new(),
};
let name_length = name_bytes.len() as u16;
buf.extend_from_slice(&name_length.to_le_bytes());
buf.extend_from_slice(&f.flags.to_le_bytes());
buf.extend_from_slice(&(f.client_data.len() as u16).to_le_bytes());
if !name_bytes.is_empty() {
buf.extend_from_slice(&name_bytes);
let padded = (name_bytes.len() + 7) & !7;
let padding = padded - name_bytes.len();
buf.extend_from_slice(&vec![0u8; padding]);
}
for &val in &f.client_data {
buf.extend_from_slice(&val.to_le_bytes());
}
if f.client_data.len() % 2 != 0 {
buf.extend_from_slice(&[0u8; 4]);
}
}
buf
}
fn serialize_v2(&self) -> Vec<u8> {
let mut buf = Vec::new();
buf.push(2); buf.push(self.filters.len() as u8);
for f in &self.filters {
buf.extend_from_slice(&f.filter_id.to_le_bytes());
if f.filter_id >= 256 {
let name_bytes = match &f.name {
Some(name) => {
let mut nb = name.as_bytes().to_vec();
nb.push(0);
nb
}
None => vec![0],
};
buf.extend_from_slice(&(name_bytes.len() as u16).to_le_bytes());
buf.extend_from_slice(&f.flags.to_le_bytes());
buf.extend_from_slice(&(f.client_data.len() as u16).to_le_bytes());
buf.extend_from_slice(&name_bytes);
} else {
buf.extend_from_slice(&f.flags.to_le_bytes());
buf.extend_from_slice(&(f.client_data.len() as u16).to_le_bytes());
}
for &val in &f.client_data {
buf.extend_from_slice(&val.to_le_bytes());
}
}
buf
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_v1_single_deflate() {
let mut buf = vec![1u8, 1]; buf.extend_from_slice(&[0u8; 6]); buf.extend_from_slice(&FILTER_DEFLATE.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&1u16.to_le_bytes()); buf.extend_from_slice(&6u32.to_le_bytes()); buf.extend_from_slice(&[0u8; 4]);
let fp = FilterPipeline::parse(&buf).unwrap();
assert_eq!(fp.version, 1);
assert_eq!(fp.filters.len(), 1);
assert_eq!(fp.filters[0].filter_id, FILTER_DEFLATE);
assert_eq!(fp.filters[0].client_data, vec![6]);
assert_eq!(fp.filters[0].name, None);
}
#[test]
fn parse_v1_shuffle_and_deflate() {
let mut buf = vec![1u8, 2];
buf.extend_from_slice(&[0u8; 6]);
buf.extend_from_slice(&FILTER_SHUFFLE.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&FILTER_DEFLATE.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&1u16.to_le_bytes());
buf.extend_from_slice(&6u32.to_le_bytes());
buf.extend_from_slice(&[0u8; 4]);
let fp = FilterPipeline::parse(&buf).unwrap();
assert_eq!(fp.filters.len(), 2);
assert_eq!(fp.filters[0].filter_id, FILTER_SHUFFLE);
assert_eq!(fp.filters[1].filter_id, FILTER_DEFLATE);
assert_eq!(fp.filters[1].client_data, vec![6]);
}
#[test]
fn parse_v2_deflate() {
let mut buf = vec![2u8, 1]; buf.extend_from_slice(&FILTER_DEFLATE.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&1u16.to_le_bytes()); buf.extend_from_slice(&6u32.to_le_bytes());
let fp = FilterPipeline::parse(&buf).unwrap();
assert_eq!(fp.version, 2);
assert_eq!(fp.filters.len(), 1);
assert_eq!(fp.filters[0].filter_id, FILTER_DEFLATE);
assert_eq!(fp.filters[0].client_data, vec![6]);
}
#[test]
fn parse_v2_three_filters() {
let mut buf = vec![2u8, 3]; buf.extend_from_slice(&FILTER_SHUFFLE.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&FILTER_DEFLATE.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&1u16.to_le_bytes());
buf.extend_from_slice(&6u32.to_le_bytes());
buf.extend_from_slice(&FILTER_FLETCHER32.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes());
let fp = FilterPipeline::parse(&buf).unwrap();
assert_eq!(fp.filters.len(), 3);
assert_eq!(fp.filters[0].filter_id, FILTER_SHUFFLE);
assert_eq!(fp.filters[1].filter_id, FILTER_DEFLATE);
assert_eq!(fp.filters[2].filter_id, FILTER_FLETCHER32);
}
#[test]
fn serialize_parse_roundtrip_v1() {
let pipeline = FilterPipeline {
version: 1,
filters: vec![
FilterDescription {
filter_id: FILTER_SHUFFLE,
name: None,
flags: 0,
client_data: vec![],
},
FilterDescription {
filter_id: FILTER_DEFLATE,
name: None,
flags: 0,
client_data: vec![6],
},
],
};
let serialized = pipeline.serialize();
let parsed = FilterPipeline::parse(&serialized).unwrap();
assert_eq!(parsed, pipeline);
}
#[test]
fn serialize_parse_roundtrip_v2() {
let pipeline = FilterPipeline {
version: 2,
filters: vec![
FilterDescription {
filter_id: FILTER_DEFLATE,
name: None,
flags: 0,
client_data: vec![9],
},
FilterDescription {
filter_id: FILTER_FLETCHER32,
name: None,
flags: 0,
client_data: vec![],
},
],
};
let serialized = pipeline.serialize();
let parsed = FilterPipeline::parse(&serialized).unwrap();
assert_eq!(parsed, pipeline);
}
#[test]
fn custom_filter_with_name_v1() {
let mut buf = vec![1u8, 1];
buf.extend_from_slice(&[0u8; 6]);
buf.extend_from_slice(&300u16.to_le_bytes());
let name = b"myfilter\0"; buf.extend_from_slice(&(name.len() as u16).to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&2u16.to_le_bytes()); buf.extend_from_slice(name);
buf.extend_from_slice(&[0u8; 7]); buf.extend_from_slice(&42u32.to_le_bytes());
buf.extend_from_slice(&99u32.to_le_bytes());
let fp = FilterPipeline::parse(&buf).unwrap();
assert_eq!(fp.filters[0].filter_id, 300);
assert_eq!(fp.filters[0].name, Some("myfilter".to_string()));
assert_eq!(fp.filters[0].client_data, vec![42, 99]);
}
#[test]
fn custom_filter_with_name_v2() {
let mut buf = vec![2u8, 1];
buf.extend_from_slice(&300u16.to_le_bytes());
let name = b"custom\0";
buf.extend_from_slice(&(name.len() as u16).to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(name);
let fp = FilterPipeline::parse(&buf).unwrap();
assert_eq!(fp.filters[0].filter_id, 300);
assert_eq!(fp.filters[0].name, Some("custom".to_string()));
}
#[test]
fn invalid_version() {
let buf = vec![3u8, 0];
let err = FilterPipeline::parse(&buf).unwrap_err();
assert_eq!(err, FormatError::InvalidFilterPipelineVersion(3));
}
}