use crate::{Error, Result};
use core::mem::size_of;
#[cfg(doc)]
use crate::ObjectIdentifier;
pub type Arc = u32;
pub(crate) const ARC_MAX_FIRST: Arc = 2;
pub(crate) const ARC_MAX_SECOND: Arc = 39;
const ARC_MAX_BYTES: usize = size_of::<Arc>();
const ARC_MAX_LAST_OCTET: u8 = 0b11110000;
pub struct Arcs<'a> {
bytes: &'a [u8],
cursor: Option<usize>,
}
impl<'a> Arcs<'a> {
pub(crate) fn new(bytes: &'a [u8]) -> Self {
Self {
bytes,
cursor: None,
}
}
pub(crate) fn try_next(&mut self) -> Result<Option<Arc>> {
match self.cursor {
None => {
let root_byte = *self.bytes.first().ok_or(Error::Empty)?;
let root = RootArcs::try_from(root_byte)?;
self.cursor = Some(0);
Ok(Some(root.first_arc()))
}
Some(0) => {
let root = RootArcs::try_from(self.bytes[0])?;
self.cursor = Some(1);
Ok(Some(root.second_arc()))
}
Some(offset) => {
let mut result = 0;
let mut arc_bytes = 0;
loop {
let len = checked_add!(offset, arc_bytes);
match self.bytes.get(len).cloned() {
#[allow(clippy::arithmetic_side_effects)]
Some(byte) => {
arc_bytes = checked_add!(arc_bytes, 1);
if (arc_bytes > ARC_MAX_BYTES) && (byte & ARC_MAX_LAST_OCTET != 0) {
return Err(Error::ArcTooBig);
}
result = result << 7 | (byte & 0b1111111) as Arc;
if byte & 0b10000000 == 0 {
self.cursor = Some(checked_add!(offset, arc_bytes));
return Ok(Some(result));
}
}
None => {
if arc_bytes == 0 {
return Ok(None);
} else {
return Err(Error::Base128);
}
}
}
}
}
}
}
}
impl<'a> Iterator for Arcs<'a> {
type Item = Arc;
fn next(&mut self) -> Option<Arc> {
self.try_next().expect("OID malformed")
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct RootArcs(u8);
impl RootArcs {
pub(crate) const fn new(first_arc: Arc, second_arc: Arc) -> Result<Self> {
if first_arc > ARC_MAX_FIRST {
return Err(Error::ArcInvalid { arc: first_arc });
}
if second_arc > ARC_MAX_SECOND {
return Err(Error::ArcInvalid { arc: second_arc });
}
#[allow(clippy::arithmetic_side_effects)]
let byte = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + second_arc as u8;
Ok(Self(byte))
}
#[allow(clippy::arithmetic_side_effects)]
pub(crate) const fn first_arc(self) -> Arc {
self.0 as Arc / (ARC_MAX_SECOND + 1)
}
#[allow(clippy::arithmetic_side_effects)]
pub(crate) const fn second_arc(self) -> Arc {
self.0 as Arc % (ARC_MAX_SECOND + 1)
}
}
impl TryFrom<u8> for RootArcs {
type Error = Error;
#[allow(clippy::arithmetic_side_effects)]
fn try_from(octet: u8) -> Result<Self> {
let first = octet as Arc / (ARC_MAX_SECOND + 1);
let second = octet as Arc % (ARC_MAX_SECOND + 1);
let result = Self::new(first, second)?;
debug_assert_eq!(octet, result.0);
Ok(result)
}
}
impl From<RootArcs> for u8 {
fn from(root_arcs: RootArcs) -> u8 {
root_arcs.0
}
}