use super::label_bytes::LabelBytes;
use crate::descriptors::{descriptor_body, write_descriptor_header};
use crate::error::{Error, Result};
use alloc::vec::Vec;
use broadcast_common::len::need_out;
use broadcast_common::{Parse, Serialize};
pub const TAG: u8 = 0x0C;
const HEADER_LEN: usize = 2;
const LABEL_LEN_FIELD: usize = 1;
const PRIORITY_LEN: usize = 1;
pub const PRIORITY_MIN: u8 = 1;
pub const PRIORITY_MAX: u8 = 100;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct PrefetchEntry<'a> {
pub label: LabelBytes<'a>,
pub prefetch_priority: u8,
}
impl PrefetchEntry<'_> {
#[must_use]
pub const fn in_range(&self) -> bool {
self.prefetch_priority >= PRIORITY_MIN && self.prefetch_priority <= PRIORITY_MAX
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct PrefetchDescriptor<'a> {
pub transport_protocol_label: u8,
pub entries: Vec<PrefetchEntry<'a>>,
}
impl<'a> Parse<'a> for PrefetchDescriptor<'a> {
type Error = crate::error::Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let body = descriptor_body(
bytes,
TAG,
"PrefetchDescriptor",
"unexpected tag for prefetch_descriptor",
)?;
let (&transport_protocol_label, mut rest) =
body.split_first().ok_or(Error::InvalidDescriptor {
tag: TAG,
reason: "prefetch_descriptor missing transport_protocol_label",
})?;
let mut entries = Vec::new();
while !rest.is_empty() {
let n = rest[0] as usize;
if rest.len() < LABEL_LEN_FIELD + n + PRIORITY_LEN {
return Err(Error::InvalidDescriptor {
tag: TAG,
reason: "prefetch_descriptor entry overruns body",
});
}
entries.push(PrefetchEntry {
label: LabelBytes::new(&rest[LABEL_LEN_FIELD..LABEL_LEN_FIELD + n]),
prefetch_priority: rest[LABEL_LEN_FIELD + n],
});
rest = &rest[LABEL_LEN_FIELD + n + PRIORITY_LEN..];
}
Ok(Self {
transport_protocol_label,
entries,
})
}
}
impl Serialize for PrefetchDescriptor<'_> {
type Error = crate::error::Error;
fn serialized_len(&self) -> usize {
HEADER_LEN
+ 1
+ self
.entries
.iter()
.map(|e| LABEL_LEN_FIELD + e.label.len() + PRIORITY_LEN)
.sum::<usize>()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let len = self.serialized_len();
need_out(buf, len)?;
write_descriptor_header(buf, TAG, len - HEADER_LEN)?;
buf[2] = self.transport_protocol_label;
let mut pos = 3;
for e in &self.entries {
buf[pos] = broadcast_common::len::fit_u8(e.label.len(), "label_length")?;
pos += LABEL_LEN_FIELD;
buf[pos..pos + e.label.len()].copy_from_slice(e.label.as_bytes());
pos += e.label.len();
buf[pos] = e.prefetch_priority;
pos += PRIORITY_LEN;
}
Ok(len)
}
}
impl<'a> crate::traits::DescriptorDef<'a> for PrefetchDescriptor<'a> {
const TAG: u8 = TAG;
const NAME: &'static str = "PREFETCH";
}
#[cfg(test)]
mod tests {
use super::*;
const BYTES: [u8; 10] = [TAG, 8, 0x01, 3, b'i', b'm', b'g', 100, 0, 1];
#[test]
fn parse_and_round_trip() {
let d = PrefetchDescriptor::parse(&BYTES).unwrap();
assert_eq!(d.transport_protocol_label, 1);
assert_eq!(d.entries.len(), 2);
assert_eq!(d.entries[0].label.as_bytes(), b"img");
assert!(d.entries[0].in_range());
assert_eq!(d.entries[1].label.as_bytes(), b"");
assert_eq!(d.entries[1].prefetch_priority, 1);
let mut out = vec![0u8; d.serialized_len()];
d.serialize_into(&mut out).unwrap();
assert_eq!(out, BYTES);
}
#[test]
fn out_of_range_priority_is_preserved() {
let bytes = [TAG, 3, 0x00, 0, 0xC8];
let d = PrefetchDescriptor::parse(&bytes).unwrap();
assert!(!d.entries[0].in_range());
let mut out = [0u8; 5];
d.serialize_into(&mut out).unwrap();
assert_eq!(out, bytes);
}
#[test]
fn empty_entry_list_and_truncation() {
let d = PrefetchDescriptor::parse(&[TAG, 1, 7]).unwrap();
assert!(d.entries.is_empty());
assert!(matches!(
&(PrefetchDescriptor::parse(&[TAG, 0])),
Err(crate::error::Error::InvalidDescriptor { .. })
));
assert!(matches!(
PrefetchDescriptor::parse(&[TAG, 3, 7, 5, b'a']),
Err(Error::InvalidDescriptor { .. })
));
let long = vec![0u8; 256];
let d = PrefetchDescriptor {
transport_protocol_label: 0,
entries: vec![PrefetchEntry {
label: LabelBytes::new(&long),
prefetch_priority: 1,
}],
};
let mut out = vec![0u8; d.serialized_len()];
assert!(matches!(
&(d.serialize_into(&mut out)),
Err(crate::error::Error::FieldOverflow(_))
));
}
}