use std::{fmt, hash, io};
use bytes::Bytes;
use crate::encode;
use crate::decode::{Constructed, DecodeError, Source};
use crate::mode::Mode;
use crate::tag::Tag;
#[derive(Clone, Debug)]
pub struct Oid<T: AsRef<[u8]> = Bytes>(pub T);
pub type ConstOid = Oid<&'static [u8]>;
impl Oid<Bytes> {
pub fn skip_in<S: Source>(
cons: &mut Constructed<S>
) -> Result<(), DecodeError<S::Error>> {
cons.take_primitive_if(Tag::OID, |prim| prim.skip_all())
}
pub fn skip_opt_in<S: Source>(
cons: &mut Constructed<S>
) -> Result<Option<()>, DecodeError<S::Error>> {
cons.take_opt_primitive_if(Tag::OID, |prim| prim.skip_all())
}
pub fn take_from<S: Source>(
constructed: &mut Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
constructed.take_primitive_if(Tag::OID, |content| {
content.take_all().map(Oid)
})
}
pub fn take_opt_from<S: Source>(
constructed: &mut Constructed<S>
) -> Result<Option<Self>, DecodeError<S::Error>> {
constructed.take_opt_primitive_if(Tag::OID, |content| {
content.take_all().map(Oid)
})
}
}
impl<T: AsRef<[u8]>> Oid<T> {
pub fn skip_if<S: Source>(
&self, constructed: &mut Constructed<S>,
) -> Result<(), DecodeError<S::Error>> {
constructed.take_primitive_if(Tag::OID, |content| {
let len = content.remaining();
content.request(len)?;
if &content.slice()[..len] == self.0.as_ref() {
content.skip_all()?;
Ok(())
}
else {
Err(content.content_err("object identifier mismatch"))
}
})
}
}
impl<T: AsRef<[u8]>> Oid<T> {
pub fn iter(&self) -> Iter {
Iter::new(self.0.as_ref())
}
}
impl<T: AsRef<[u8]>> AsRef<[u8]> for Oid<T> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<T: AsRef<[u8]>, U: AsRef<[u8]>> PartialEq<Oid<U>> for Oid<T> {
fn eq(&self, other: &Oid<U>) -> bool {
self.0.as_ref() == other.0.as_ref()
}
}
impl<T: AsRef<[u8]>> Eq for Oid<T> { }
impl<T: AsRef<[u8]>> hash::Hash for Oid<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.0.as_ref().hash(state)
}
}
impl<T: AsRef<[u8]>> fmt::Display for Oid<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut components = self.iter();
write!(f, "{}", components.next().unwrap().to_u32().unwrap())?;
for component in components {
if let Some(val) = component.to_u32() {
write!(f, ".{}", val)?;
}
else {
write!(f, ".(not implemented)")?;
}
}
Ok(())
}
}
impl<T: AsRef<[u8]>> encode::PrimitiveContent for Oid<T> {
const TAG: Tag = Tag::OID;
fn encoded_len(&self, _: Mode) -> usize {
self.0.as_ref().len()
}
fn write_encoded<W: io::Write>(
&self,
_: Mode,
target: &mut W
) -> Result<(), io::Error> {
target.write_all(self.0.as_ref())
}
}
#[derive(Clone, Copy, Debug)]
pub struct Component<'a> {
position: Position,
slice: &'a [u8],
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
enum Position {
First,
Second,
Other,
}
impl<'a> Component<'a> {
fn new(position: Position, slice: &'a [u8]) -> Self {
Component { position, slice }
}
pub fn to_u32(self) -> Option<u32> {
if self.slice.len() > 5
|| (self.slice.len() == 4 && self.slice[0] & 0x70 != 0)
{
return None
}
let mut res = 0;
for &ch in self.slice {
res = res << 7 | u32::from(ch & 0x7F);
}
match self.position {
Position::First => {
if res < 40 {
Some(0)
}
else if res < 80{
Some(1)
}
else {
Some(2)
}
}
Position::Second => {
if res < 80 {
Some(res % 40)
}
else {
Some(res - 80)
}
}
Position::Other => Some(res)
}
}
}
impl<'a> PartialEq for Component<'a> {
fn eq(&self, other: &Self) -> bool {
self.position == other.position && self.slice == other.slice
}
}
impl<'a> Eq for Component<'a> { }
pub struct Iter<'a> {
slice: &'a [u8],
position: Position,
}
impl<'a> Iter<'a> {
fn new(slice: &'a [u8]) -> Self {
Iter {
slice,
position: Position::First
}
}
fn advance_position(&mut self) -> Position {
let res = self.position;
self.position = match res {
Position::First => Position::Second,
_ => Position::Other
};
res
}
}
impl<'a> Iterator for Iter<'a> {
type Item = Component<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.slice.is_empty() {
return None
}
for i in 0..self.slice.len() {
if self.slice[i] & 0x80 == 0 {
let (res, tail) = self.slice.split_at(i + 1);
if self.position != Position::First {
self.slice = tail;
}
return Some(Component::new(self.advance_position(), res));
}
}
panic!("illegal object identifier (last octet has bit 8 set)");
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn display() {
assert_eq!(
"2.5.29.19",
format!("{}", Oid(&[85, 29, 19])).as_str()
);
}
}