use alloc::vec;
use alloc::vec::Vec;
use super::errors::__undecodable_ie;
use super::read;
use super::{CriticalityOf, Field, Message, Pdu, Protocol, Transfer, object_set};
use crate::check::{Deviation, Wrong, deviations, structure_deviations};
use crate::{Decode, Error, ErrorKind, Faults, Octets, Reader, Writer};
fn message_ies<'o, P: Protocol>(octets: &'o [u8]) -> Result<Vec<P::Field<'o>>, Error> {
let mut reader = Reader::new(octets);
let extended = reader.bit()?;
let ies = (|| {
let count = reader.size(0, 65535)?;
let mut list = reader.list(count);
for _ in 0..count {
list.push(P::Field::decode(&mut reader)?);
}
Ok::<_, Error>(list)
})();
let ies = ies.map_err(|error| error.in_field("protocol_ies"))?;
if extended {
reader.skip_additions()?;
}
reader.finish()?;
Ok(ies)
}
fn message_faults<P: Protocol>(octets: &[u8]) -> Faults {
let mut reader = Reader::noting(octets);
let read = (|| {
let extended = reader.bit()?;
let count = reader.size(0, 65535)?;
for _ in 0..count {
P::Field::decode(&mut reader)?;
}
if extended {
reader.skip_additions()?;
}
reader.align();
reader.finish()
})();
match read {
Ok(()) => *reader.faults(),
Err(_) => Faults::default(),
}
}
fn listed_ies<P: Protocol>(octets: &[u8]) -> Result<Vec<(u16, CriticalityOf<P>)>, Error> {
let (extended, count) = read::head(octets)?;
let mut listed = Vec::with_capacity(count.min(octets.len() / 4));
let mut at = 3;
for _ in 0..count {
let (id, criticality, _, next) =
read::field::<P>(octets, at).map_err(|error| error.in_field("protocol_ies"))?;
listed.push((id, criticality));
at = next;
}
read::tail(octets, at, extended)?;
Ok(listed)
}
pub fn message_octets<'f, F: Field<'f>>(ies: &[F]) -> Result<Vec<u8>, Error> {
let Ok(count) = u16::try_from(ies.len()) else {
return message_octets_as_its_type(ies);
};
let values: usize = ies.iter().map(|ie| ie.value().len() + 5).sum();
let mut octets = Vec::with_capacity(3 + values);
octets.push(0);
octets.extend_from_slice(&count.to_be_bytes());
for ie in ies {
let [high, low] = ie.id().to_be_bytes();
let criticality: crate::Criticality = ie.criticality().into();
let criticality = (criticality as u8) << 6;
let value: &[u8] = ie.value();
match value.len() {
length @ 0..=0x7f => octets.extend_from_slice(&[high, low, criticality, length as u8]),
length @ 0x80..=0x3fff => {
let [first, second] = (length as u16 | 0x8000).to_be_bytes();
octets.extend_from_slice(&[high, low, criticality, first, second]);
}
_ => {
octets.extend_from_slice(&[high, low, criticality]);
Writer::new(&mut octets).open_type(value);
continue;
}
}
octets.extend_from_slice(value);
}
Ok(octets)
}
pub fn message_octets_as_its_type<'f, F: Field<'f>>(ies: &[F]) -> Result<Vec<u8>, Error> {
let values: usize = ies.iter().map(|ie| ie.value().len() + 5).sum();
let mut octets = Vec::with_capacity(3 + values);
let mut writer = Writer::new(&mut octets);
writer.bit(false);
writer.size(ies.len(), 0, 65535)?;
for ie in ies {
ie.encode(&mut writer)?;
}
Ok(octets)
}
fn message_of_protocol_ies<'p, X: Pdu<'p>>(pdu: &X) -> Result<Option<&'static str>, Error> {
let set = object_set::<X::Protocol>(pdu.direction(), pdu.procedure_code());
match set {
Some(set) if set.ies.is_none() => Err(Error::no_protocol_ies(set.name)),
set => Ok(set.map(|set| set.name)),
}
}
pub fn protocol_ies<'s, 'p: 's, X: Pdu<'p>>(
pdu: &'s X,
) -> Result<Vec<<X::Protocol as Protocol>::Field<'s>>, Error> {
let name = message_of_protocol_ies(pdu)?;
let value: &'s [u8] = pdu.value().as_slice();
if value.is_empty() {
let none = Error::new(ErrorKind::Decode, "there are no octets");
return Err(none.of_message(name));
}
message_ies::<X::Protocol>(value).map_err(|error| error.of_message(name))
}
pub fn set_protocol_ies<'p, 'f, X: Pdu<'p>>(
pdu: &mut X,
ies: &[<X::Protocol as Protocol>::Field<'f>],
) -> Result<(), Error> {
message_of_protocol_ies(pdu)?;
let octets = message_octets(ies).map_err(|error| error.of_protocol_ies(ies.len()))?;
pdu.__set_value(Octets::owned(octets));
Ok(())
}
pub fn of_message<'m, M: Message<'m>>(
message: &M,
criticality: CriticalityOf<M::Protocol>,
) -> Result<<M::Protocol as Protocol>::Pdu<'static>, Error> {
let octets = Octets::owned(crate::encode(message)?);
let pdu = <<M::Protocol as Protocol>::Pdu<'static> as Pdu<'static>>::__of(
M::DIRECTION,
M::PROCEDURE_CODE,
criticality,
octets,
);
pdu.ok_or_else(|| Error::new(ErrorKind::Encode, "a message has no direction"))
}
pub fn holds_key<P: Protocol>(direction: &str, code: u8) -> bool {
let ies = object_set::<P>(direction, code).and_then(|set| set.ies);
ies.is_some_and(|ies| ies.iter().any(|(id, ..)| P::__KEY_IES.contains(id)))
}
pub fn check<'p, X: Pdu<'p>>(pdu: &X) -> Result<Vec<Deviation<X::Protocol>>, Error> {
checked(pdu, true)
}
pub fn check_structure<'p, X: Pdu<'p>>(pdu: &X) -> Result<Vec<Deviation<X::Protocol>>, Error> {
checked(pdu, false)
}
fn checked<'p, X: Pdu<'p>>(pdu: &X, values: bool) -> Result<Vec<Deviation<X::Protocol>>, Error> {
let (direction, code) = (pdu.direction(), pdu.procedure_code());
let criticality = pdu.criticality();
let Some(message) = object_set::<X::Protocol>(direction, code) else {
return Ok(vec![Deviation::UnknownProcedure {
procedure_code: code,
direction,
criticality,
}]);
};
let mut found = Vec::new();
if let Some(assigned) = X::Protocol::__procedure_criticality(code)
&& criticality != assigned
{
found.push(Deviation::PduCriticality {
criticality,
assigned,
});
}
let Some(set) = message.ies else {
return Ok(found);
};
let octets = pdu.value().as_slice();
let checked = container_deviations(message.name, set, octets, values, &mut found);
checked.map_err(|error| error.of_message(Some(message.name)))?;
Ok(found)
}
pub fn check_transfer<'a, T: Transfer<'a>>(
octets: &[u8],
) -> Result<Vec<Deviation<T::Protocol>>, Error> {
transfer_checked::<T>(octets, true)
}
pub fn check_transfer_structure<'a, T: Transfer<'a>>(
octets: &[u8],
) -> Result<Vec<Deviation<T::Protocol>>, Error> {
transfer_checked::<T>(octets, false)
}
fn transfer_checked<'a, T: Transfer<'a>>(
octets: &[u8],
values: bool,
) -> Result<Vec<Deviation<T::Protocol>>, Error> {
let mut found = Vec::new();
let checked = container_deviations(T::NAME, T::__OBJECT_SET, octets, values, &mut found);
checked.map_err(|error| error.decoding(T::NAME))?;
Ok(found)
}
fn container_deviations<P: Protocol>(
name: &'static str,
set: &'static [(u16, CriticalityOf<P>, bool)],
octets: &[u8],
values: bool,
found: &mut Vec<Deviation<P>>,
) -> Result<(), Error> {
if octets.is_empty() {
return Err(Error::new(ErrorKind::Decode, "there are no octets"));
}
if !values {
let listed = listed_ies::<P>(octets)?;
structure_deviations(set, &listed, found);
return Ok(());
}
let ies = message_ies::<P>(octets)?;
let faults = message_faults::<P>(octets);
found.extend(faults.iter().map(|noted| Deviation::NotCanonical {
ie: None,
fault: noted.fault,
bit: noted.bit,
}));
let listed: Vec<_> = ies.iter().map(|ie| (ie.id(), ie.criticality())).collect();
let value = |position: usize| {
let ie = &ies[position];
let (id, criticality) = (ie.id(), ie.criticality());
match P::__decodes(id, ie.value()) {
None => (None, Faults::default()),
Some(Ok((outside, faults))) => (outside.map(Wrong::OutsideRoot), faults),
Some(Err(error)) => {
let error = __undecodable_ie::<P>(error, name, id, criticality, position);
(Some(Wrong::Undecodable(error)), Faults::default())
}
}
};
deviations(set, &listed, &value, found);
Ok(())
}