use alloc::borrow::Cow;
use core::marker::PhantomData;
use super::errors::{__undecodable_ie, __wrong_message};
use super::{Container, CriticalityOf, Field, Ie, InProtocol, List, Message, Pdu, Protocol};
use crate::{Decode, Error, Reader};
pub struct Source<'s, P: Protocol> {
message: &'static str,
fields: Fields<'s, P>,
}
enum Fields<'s, P: Protocol> {
Octets(&'s [u8]),
Decoded(&'s [P::Field<'s>]),
Failed(Error),
}
impl<'s, P: Protocol> Source<'s, P> {
#[doc(hidden)]
pub fn __of_octets(message: &'static str, octets: &'s [u8]) -> Self {
let fields = Fields::Octets(octets);
Self { message, fields }
}
#[doc(hidden)]
pub fn __of_fields(message: &'static str, fields: &'s [P::Field<'s>]) -> Self {
let fields = Fields::Decoded(fields);
Self { message, fields }
}
#[doc(hidden)]
pub fn __failed(message: &'static str, error: Error) -> Self {
let fields = Fields::Failed(error);
Self { message, fields }
}
#[doc(hidden)]
pub fn __of_pdu<X: Pdu<'s> + InProtocol<Protocol = P>>(pdu: &'s X) -> Source<'s, P> {
let set = super::object_set::<P>(pdu.direction(), pdu.procedure_code());
match set {
Some(set) if set.ies.is_none() => {
Self::__failed(set.name, Error::no_protocol_ies(set.name))
}
set => {
let name = set.map_or("message", |set| set.name);
let value = pdu.value();
Self::__of_octets(name, value.as_slice())
}
}
}
pub fn ies<T: Ie<Protocol = P>>(self) -> Ies<'s, T> {
let message = self.message;
Ies {
walk: self.walk(),
message,
ie: PhantomData,
}
}
pub fn ie<T: Ie<Protocol = P>>(self) -> Option<Result<T::Value<'s>, Error>> {
self.ies::<T>().next()
}
pub fn items<L: List<Protocol = P>>(self) -> Items<'s, L> {
let message = self.message;
let state = match self.ies_entries::<L>() {
None => State::Done,
Some(Err(error)) => State::Failed(Some(error)),
Some(Ok(entry)) => match Walking::of(entry, message, L::__SIZE) {
Ok(walking) => State::Walking(walking),
Err(error) => State::Failed(Some(error)),
},
};
Items {
state,
list: PhantomData,
}
}
fn ies_entries<T: Ie<Protocol = P>>(self) -> Option<Result<__Entry<'s, P>, Error>> {
let mut walk = self.walk();
loop {
match walk.next_of(T::ID)? {
Ok(entry) if entry.id == T::ID => return Some(Ok(entry)),
Ok(_) => {}
Err(error) => return Some(Err(error)),
}
}
}
#[doc(hidden)]
#[inline]
pub fn __walk(self) -> __Walk<'s, P> {
self.walk()
}
#[inline]
fn walk(self) -> __Walk<'s, P> {
match self.fields {
Fields::Octets(octets) => __Walk::of_octets(self.message, octets),
Fields::Decoded(fields) => __Walk::Decoded {
fields,
position: 0,
},
Fields::Failed(error) => __Walk::Failed(Some(error)),
}
}
}
#[derive(Clone, Copy)]
pub(super) enum Value<'s> {
Whole(&'s [u8]),
Fragmented(&'s [u8]),
}
impl<'s> Value<'s> {
fn octets<P: Protocol>(&self) -> Result<Cow<'s, [u8]>, Error> {
match self {
Value::Whole(octets) => Ok(Cow::Borrowed(octets)),
Value::Fragmented(field) => {
let field = P::Field::decode(&mut Reader::new(field))?;
Ok(Cow::Owned(field.value().to_vec()))
}
}
}
}
#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct __Entry<'s, P: Protocol> {
position: usize,
id: u16,
criticality: CriticalityOf<P>,
value: Value<'s>,
}
impl<'s, P: Protocol> __Entry<'s, P> {
#[inline]
pub fn id(&self) -> u16 {
self.id
}
#[inline]
pub fn decode<T: Ie<Protocol = P>>(&self, message: &str) -> Result<T::Value<'s>, Error> {
let read = match self.value {
Value::Whole(octets) => crate::decode::<T::Value<'s>>(octets),
Value::Fragmented(_) => self
.value
.octets::<P>()
.and_then(|octets| gathered::<T>(&octets)),
};
match read {
Ok(value) => Ok(value),
Err(error) => Err(self.undecodable(error, message)),
}
}
}
#[cold]
#[inline(never)]
fn gathered<'s, T: Ie>(octets: &[u8]) -> Result<T::Value<'s>, Error> {
T::__owned(octets)
}
#[doc(hidden)]
pub enum __Walk<'s, P: Protocol> {
Octets {
octets: &'s [u8],
at: usize,
message: &'static str,
left: usize,
position: usize,
extended: bool,
},
Decoded {
fields: &'s [P::Field<'s>],
position: usize,
},
Failed(Option<Error>),
}
#[inline(always)]
pub(super) fn field<'s, P: Protocol>(
octets: &'s [u8],
at: usize,
) -> Result<(u16, CriticalityOf<P>, Value<'s>, usize), Error> {
if let [high, low, criticality, rest @ ..] = &octets[at..]
&& let Some(criticality) = criticality_of(*criticality >> 6)
{
let (length, header) = match rest {
[length @ 0..=0x7f, ..] => (usize::from(*length), 4),
[first @ 0x80..=0xbf, second, ..] => {
(usize::from(first & 0x3f) << 8 | usize::from(*second), 5)
}
_ => (usize::MAX, 0),
};
let start = at + header;
if header > 0 && length <= octets.len() - start {
let id = u16::from_be_bytes([*high, *low]);
let value = Value::Whole(&octets[start..start + length]);
return Ok((id, criticality.into(), value, start + length));
}
}
unusual::<P>(octets, at)
}
#[cold]
#[inline(never)]
fn unusual<'s, P: Protocol>(
octets: &'s [u8],
at: usize,
) -> Result<(u16, CriticalityOf<P>, Value<'s>, usize), Error> {
let mut reader = Reader::new(&octets[at..]);
let field = P::Field::decode(&mut reader)?;
let next = at + reader.position() / 8;
let value = match field.value().as_borrowed() {
Some(value) => Value::Whole(value),
None => Value::Fragmented(&octets[at..next]),
};
Ok((field.id(), field.criticality(), value, next))
}
#[inline]
fn skip(octets: &[u8], at: usize) -> Option<(u16, usize)> {
let [high, low, criticality, rest @ ..] = octets.get(at..)? else {
return None;
};
if criticality >> 6 == 3 {
return None;
}
let (length, header) = match rest {
[length @ 0..=0x7f, ..] => (usize::from(*length), 4),
[first @ 0x80..=0xbf, second, ..] => {
(usize::from(first & 0x3f) << 8 | usize::from(*second), 5)
}
_ => return None,
};
let next = at + header + length;
(next <= octets.len()).then_some((u16::from_be_bytes([*high, *low]), next))
}
#[inline]
fn criticality_of(index: u8) -> Option<crate::Criticality> {
match index {
0 => Some(crate::Criticality::Reject),
1 => Some(crate::Criticality::Ignore),
2 => Some(crate::Criticality::Notify),
_ => None,
}
}
impl<'s, P: Protocol> __Walk<'s, P> {
fn of_octets(message: &'static str, octets: &'s [u8]) -> Self {
match head(octets) {
Ok((extended, left)) => Self::Octets {
octets,
at: 3,
message,
left,
position: 0,
extended,
},
Err(error) => Self::Failed(Some(error.of_message(Some(message)))),
}
}
#[inline]
fn next_of(&mut self, id: u16) -> Option<Result<__Entry<'s, P>, Error>> {
match self {
Self::Decoded { fields, position } => {
let found = fields[*position..]
.iter()
.position(|field| field.id() == id);
let Some(found) = found else {
*position = fields.len();
return None;
};
*position += found;
self.next()
}
Self::Octets {
octets,
at,
left,
position,
..
} => {
let (mut here, mut count, mut index) = (*at, *left, *position);
while count > 0 {
let Some((found, next)) = skip(octets, here) else {
break;
};
if found == id {
break;
}
here = next;
count -= 1;
index += 1;
}
(*at, *left, *position) = (here, count, index);
self.next()
}
Self::Failed(_) => self.next(),
}
}
#[inline(always)]
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<Result<__Entry<'s, P>, Error>> {
match self {
Self::Decoded { fields, position } => {
let field = fields.get(*position)?;
let entry = __Entry {
position: *position,
id: field.id(),
criticality: field.criticality(),
value: Value::Whole(field.value().as_slice()),
};
*position += 1;
Some(Ok(entry))
}
Self::Failed(error) => error.take().map(Err),
Self::Octets {
octets,
at,
message,
left,
position,
extended,
} => {
let message = *message;
if *left == 0 {
let end = tail(octets, *at, *extended);
let end = end.map_err(|error| error.of_message(Some(message)));
*self = Self::Failed(end.err());
return self.next();
}
match field::<P>(octets, *at) {
Ok((id, criticality, value, next)) => {
let entry = __Entry {
position: *position,
id,
criticality,
value,
};
*at = next;
*left -= 1;
*position += 1;
Some(Ok(entry))
}
Err(error) => {
let error = error.in_field("protocol_ies").of_message(Some(message));
*self = Self::Failed(Some(error));
self.next()
}
}
}
}
}
}
pub struct Ies<'s, T: Ie> {
walk: __Walk<'s, T::Protocol>,
message: &'static str,
ie: PhantomData<T>,
}
impl<'s, T: Ie> Iterator for Ies<'s, T> {
type Item = Result<T::Value<'s>, Error>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.walk.next_of(T::ID)? {
Ok(entry) if entry.id == T::ID => return Some(entry.decode::<T>(self.message)),
Ok(_) => {}
Err(error) => return Some(Err(error)),
}
}
}
}
pub struct Items<'s, L: List> {
state: State<'s, L::Protocol>,
list: PhantomData<L>,
}
enum State<'s, P: Protocol> {
Walking(Walking<'s, P>),
Failed(Option<Error>),
Done,
}
struct Walking<'s, P: Protocol> {
list: __Entry<'s, P>,
octets: Cow<'s, [u8]>,
message: &'static str,
at: usize,
left: usize,
index: usize,
}
impl<'s, P: Protocol> Walking<'s, P> {
fn of(
list: __Entry<'s, P>,
message: &'static str,
size: (usize, usize),
) -> Result<Self, Error> {
let octets = match list.value.octets::<P>() {
Ok(octets) => octets,
Err(error) => return Err(list.undecodable(error, message)),
};
let mut reader = Reader::new(&octets);
match reader.size(size.0, size.1) {
Ok(left) => {
let at = reader.position().div_ceil(8);
Ok(Self {
list,
octets,
message,
at,
left,
index: 0,
})
}
Err(error) => Err(list.undecodable(error, message)),
}
}
fn advance(&mut self, used: usize) -> usize {
self.at += used;
self.left -= 1;
self.index += 1;
self.index - 1
}
fn item_undecodable(
&self,
error: Error,
id: u16,
criticality: CriticalityOf<P>,
index: usize,
) -> Error {
let list = super::ie_name::<P>(self.list.id);
__undecodable_ie::<P>(error, &list, id, criticality, index)
}
}
impl<'s, P: Protocol> __Entry<'s, P> {
#[cold]
fn undecodable(&self, error: Error, message: &str) -> Error {
__undecodable_ie::<P>(error, message, self.id, self.criticality, self.position)
}
}
impl<'s, L: List> Iterator for Items<'s, L> {
type Item = Result<<L::Item as Ie>::Value<'s>, Error>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let walking = match &mut self.state {
State::Walking(walking) => walking,
State::Failed(error) => return error.take().map(Err),
State::Done => return None,
};
if walking.left == 0 {
let end = Reader::new(&walking.octets[walking.at..]).finish();
let end = end.map_err(|error| walking.list.undecodable(error, walking.message));
self.state = State::Failed(end.err());
continue;
}
let read = match &walking.octets {
Cow::Borrowed(octets) => {
let octets: &'s [u8] = octets;
field::<L::Protocol>(octets, walking.at).map(
|(id, criticality, value, next)| {
let value = (id == L::Item::ID).then(|| match value {
Value::Whole(value) => crate::decode(value),
value => value
.octets::<L::Protocol>()
.and_then(|octets| L::Item::__owned(&octets)),
});
(value, id, criticality, next)
},
)
}
Cow::Owned(octets) => field::<L::Protocol>(octets, walking.at).map(
|(id, criticality, value, next)| {
let value = (id == L::Item::ID).then(|| {
let octets = value.octets::<L::Protocol>();
octets.and_then(|octets| L::Item::__owned(&octets))
});
(value, id, criticality, next)
},
),
};
let read = read.map(|(value, id, criticality, next)| {
let index = walking.advance(next - walking.at);
value.map(|value| (value, id, criticality, index))
});
match read {
Ok(None) => {}
Ok(Some((Ok(value), ..))) => return Some(Ok(value)),
Ok(Some((Err(error), id, criticality, index))) => {
return Some(Err(walking.item_undecodable(error, id, criticality, index)));
}
Err(error) => {
let error = walking.list.undecodable(error, walking.message);
self.state = State::Failed(None);
return Some(Err(error));
}
}
}
}
}
#[doc(hidden)]
pub fn __first<'s, 'a: 's, T: Ie, M: Container<'a> + InProtocol<Protocol = T::Protocol>>(
container: &'s M,
) -> Result<Option<(usize, T::Value<'s>)>, Error> {
let source = Source::<T::Protocol>::__of_fields(M::NAME, container.__fields());
match source.ies_entries::<T>() {
None => Ok(None),
Some(Err(error)) => Err(error),
Some(Ok(entry)) => entry
.decode::<T>(M::NAME)
.map(|value| Some((entry.position, value))),
}
}
pub fn items_of_fields<'s, L: List>(
message: &'static str,
fields: &'s [<L::Protocol as Protocol>::Field<'s>],
) -> Items<'s, L> {
Source::__of_fields(message, fields).items::<L>()
}
#[diagnostic::on_unimplemented(
message = "the IEs are read from a `{Self}`, which is no `{N}`, nor a PDU that holds one",
label = "a `{N}`, or a PDU that holds one, is expected here",
note = "the name after the first argument of the macro is the type of the message or of \
the transfer that the IEs are read from"
)]
#[doc(hidden)]
pub trait __Holds<N> {
fn __message(&self) -> Result<&'static str, Error>;
}
impl<'a, M: Container<'a>> __Holds<M> for M {
#[inline]
fn __message(&self) -> Result<&'static str, Error> {
Ok(M::NAME)
}
}
#[doc(hidden)]
pub trait __Item<'s>: Clone {
type Protocol: Protocol;
fn id(&self) -> u16;
fn decode<T: Ie<Protocol = Self::Protocol>>(
&self,
message: &str,
) -> Result<T::Value<'s>, Error>;
}
impl<'s, P: Protocol> __Item<'s> for __Entry<'s, P> {
type Protocol = P;
#[inline]
fn id(&self) -> u16 {
self.id
}
#[inline]
fn decode<T: Ie<Protocol = P>>(&self, message: &str) -> Result<T::Value<'s>, Error> {
__Entry::decode::<T>(self, message)
}
}
#[doc(hidden)]
pub struct __Decoded<'s, P: Protocol> {
position: usize,
field: &'s P::Field<'s>,
}
impl<P: Protocol> Clone for __Decoded<'_, P> {
#[inline]
fn clone(&self) -> Self {
Self {
position: self.position,
field: self.field,
}
}
}
impl<'s, P: Protocol> __Item<'s> for __Decoded<'s, P> {
type Protocol = P;
#[inline]
fn id(&self) -> u16 {
self.field.id()
}
#[inline]
fn decode<T: Ie<Protocol = P>>(&self, message: &str) -> Result<T::Value<'s>, Error> {
let octets: &'s [u8] = self.field.value().as_slice();
match crate::decode::<T::Value<'s>>(octets) {
Ok(value) => Ok(value),
Err(error) => Err(self.undecodable(error, message)),
}
}
}
impl<P: Protocol> __Decoded<'_, P> {
#[cold]
fn undecodable(&self, error: Error, message: &str) -> Error {
let (id, criticality) = (self.field.id(), self.field.criticality());
__undecodable_ie::<P>(error, message, id, criticality, self.position)
}
}
#[doc(hidden)]
pub trait __Each<'s> {
type Item: __Item<'s>;
fn __each(self, each: impl FnMut(Self::Item)) -> Result<(), Error>;
}
impl<'s, P: Protocol> __Each<'s> for Source<'s, P> {
type Item = __Entry<'s, P>;
#[inline]
fn __each(self, mut each: impl FnMut(__Entry<'s, P>)) -> Result<(), Error> {
let message = self.message;
let octets = match self.fields {
Fields::Octets(octets) => octets,
Fields::Failed(error) => return Err(error),
Fields::Decoded(fields) => {
for (position, field) in fields.iter().enumerate() {
each(__Entry {
position,
id: field.id(),
criticality: field.criticality(),
value: Value::Whole(field.value().as_slice()),
});
}
return Ok(());
}
};
let (extended, count) = head(octets).map_err(|error| error.of_message(Some(message)))?;
let mut at = 3;
for position in 0..count {
let (id, criticality, value, next) = match field::<P>(octets, at) {
Ok(field) => field,
Err(error) => {
return Err(error.in_field("protocol_ies").of_message(Some(message)));
}
};
at = next;
each(__Entry {
position,
id,
criticality,
value,
});
}
tail(octets, at, extended).map_err(|error| error.of_message(Some(message)))
}
}
#[inline]
pub(super) fn head(octets: &[u8]) -> Result<(bool, usize), Error> {
match octets {
[] => Err(Error::new(crate::ErrorKind::Decode, "there are no octets")),
[first, high, low, ..] => {
let count = usize::from(u16::from_be_bytes([*high, *low]));
Ok((first & 0x80 != 0, count))
}
_ => {
let mut reader = Reader::new(octets);
let extended = reader.bit()?;
let count = reader.size(0, 65535);
Ok((
extended,
count.map_err(|error| error.in_field("protocol_ies"))?,
))
}
}
}
#[inline]
pub(super) fn tail(octets: &[u8], at: usize, extended: bool) -> Result<(), Error> {
if !extended && at == octets.len() {
return Ok(());
}
let mut reader = Reader::new(octets);
reader.octets(at)?;
if extended {
reader.skip_additions()?;
}
reader.finish()
}
#[doc(hidden)]
pub struct __Fields<'s, P: Protocol> {
fields: &'s [P::Field<'s>],
}
impl<'s, P: Protocol> __Each<'s> for __Fields<'s, P> {
type Item = __Decoded<'s, P>;
#[inline]
fn __each(self, mut each: impl FnMut(__Decoded<'s, P>)) -> Result<(), Error> {
for (position, field) in self.fields.iter().enumerate() {
each(__Decoded { position, field });
}
Ok(())
}
}
#[doc(hidden)]
pub trait __Source<'s> {
type Protocol: Protocol;
type Walk: __Each<'s>;
fn __source<N>(&'s self) -> Result<Self::Walk, Error>
where
Self: __Holds<N>;
}
impl<'s, 'a: 's, M: Container<'a>> __Source<'s> for M {
type Protocol = <M as InProtocol>::Protocol;
type Walk = __Fields<'s, <M as InProtocol>::Protocol>;
#[inline]
fn __source<N>(&'s self) -> Result<Self::Walk, Error>
where
Self: __Holds<N>,
{
<Self as __Holds<N>>::__message(self)?;
Ok(__Fields {
fields: self.__fields(),
})
}
}
#[doc(hidden)]
pub fn __pdu_holds<'p, 'a, X: Pdu<'p>, M: Message<'a, Protocol = X::Protocol>>(
pdu: &X,
) -> Result<&'static str, Error> {
let (direction, code) = (pdu.direction(), pdu.procedure_code());
if (direction, code) != (M::DIRECTION, M::PROCEDURE_CODE) {
return Err(__wrong_message::<X::Protocol>(M::NAME, direction, code));
}
if !M::__OF_PROTOCOL_IES {
return Err(Error::no_protocol_ies(M::NAME));
}
Ok(M::NAME)
}