use crate::{ClassTag, Error, Id, PCTag, TagNumber};
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::io::{Read, Write};
use std::mem;
pub const LONG_FLAG: u8 = 0x1f;
pub const MAX_SHORT: u8 = LONG_FLAG - 1;
pub const MORE_FLAG: u8 = 0x80;
pub const CLASS_MASK: u8 = 0xc0;
pub const PC_MASK: u8 = 0x20;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IdRef {
bytes: [u8],
}
impl IdRef {
pub fn parse<'a>(bytes: &mut &'a [u8]) -> Result<&'a Self, Error> {
let mut readable: &[u8] = bytes;
unsafe {
let len = parse_id(&mut readable, &mut std::io::sink())?;
let ret = Self::from_bytes_unchecked(&bytes[..len]);
*bytes = readable;
Ok(ret)
}
}
pub fn parse_mut<'a>(bytes: &mut &'a mut [u8]) -> Result<&'a mut Self, Error> {
let read_bytes = {
let mut readable: &[u8] = *bytes;
unsafe { parse_id(&mut readable, &mut std::io::sink())? }
};
unsafe {
let init_ptr = bytes.as_mut_ptr();
let left_bytes = bytes.len() - read_bytes;
*bytes = std::slice::from_raw_parts_mut(init_ptr.add(read_bytes), left_bytes);
let read = std::slice::from_raw_parts_mut(init_ptr, read_bytes);
Ok(Self::from_mut_bytes_unchecked(read))
}
}
pub const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
mem::transmute(bytes)
}
pub unsafe fn from_mut_bytes_unchecked(bytes: &mut [u8]) -> &mut Self {
mem::transmute(bytes)
}
pub const fn eoc() -> &'static Self {
const BYTES: [u8; 1] = [0x00];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn boolean() -> &'static Self {
const BYTES: [u8; 1] = [0x01];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn integer() -> &'static Self {
const BYTES: [u8; 1] = [0x02];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn octet_string() -> &'static Self {
const BYTES: [u8; 1] = [0x04];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn octet_string_constructed() -> &'static Self {
const BYTES: [u8; 1] = [0x24];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn null() -> &'static Self {
const BYTES: [u8; 1] = [0x05];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn real() -> &'static Self {
const BYTES: [u8; 1] = [0x09];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn utf8_string() -> &'static Self {
const BYTES: [u8; 1] = [0x0c];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn utf8_string_constructed() -> &'static Self {
const BYTES: [u8; 1] = [0x2c];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn sequence() -> &'static Self {
const BYTES: [u8; 1] = [0x30];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn set() -> &'static Self {
const BYTES: [u8; 1] = [0x31];
unsafe { Self::from_bytes_unchecked(&BYTES as &[u8]) }
}
pub const fn len(&self) -> usize {
self.bytes.len()
}
pub const fn class(&self) -> ClassTag {
if self.is_universal() {
ClassTag::Universal
} else if self.is_application() {
ClassTag::Application
} else if self.is_context_specific() {
ClassTag::ContextSpecific
} else {
ClassTag::Private
}
}
pub const fn is_universal(&self) -> bool {
let first = self.bytes[0];
first & CLASS_MASK == ClassTag::Universal as u8
}
pub const fn is_application(&self) -> bool {
let first = self.bytes[0];
first & CLASS_MASK == ClassTag::Application as u8
}
pub const fn is_context_specific(&self) -> bool {
let first = self.bytes[0];
first & CLASS_MASK == ClassTag::ContextSpecific as u8
}
pub const fn is_private(&self) -> bool {
let first = self.bytes[0];
first & CLASS_MASK == ClassTag::Private as u8
}
pub const fn pc(&self) -> PCTag {
if self.is_primitive() {
PCTag::Primitive
} else {
PCTag::Constructed
}
}
pub const fn is_primitive(&self) -> bool {
let first = self.bytes[0];
first & PC_MASK == PCTag::Primitive as u8
}
pub const fn is_constructed(&self) -> bool {
let first = self.bytes[0];
first & PC_MASK == PCTag::Constructed as u8
}
pub fn number(&self) -> Result<TagNumber, Error> {
debug_assert!(0 < self.len());
if self.bytes.len() == 1 {
let ret = self.bytes[0] & LONG_FLAG;
Ok(ret.into())
} else {
debug_assert_eq!(self.bytes[0] & LONG_FLAG, LONG_FLAG);
let mut ret: u128 = 0;
for &octet in self.as_ref()[1..].iter() {
const MASK: u8 = !MORE_FLAG;
const SHIFT_MUL: u128 = 128;
ret = ret.checked_mul(SHIFT_MUL).ok_or(Error::OverFlow)?;
ret += (octet & MASK) as u128;
}
Ok(ret.into())
}
}
pub fn set_class(&mut self, cls: ClassTag) {
let bytes = &mut self.bytes;
bytes[0] &= !CLASS_MASK;
bytes[0] |= cls as u8;
}
pub fn set_pc(&mut self, pc: PCTag) {
let bytes = &mut self.bytes;
const MASK: u8 = !PC_MASK;
bytes[0] &= MASK;
bytes[0] |= pc as u8;
}
}
impl AsRef<[u8]> for IdRef {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl ToOwned for IdRef {
type Owned = Id;
fn to_owned(&self) -> Self::Owned {
unsafe { Self::Owned::from_bytes_unchecked(self.as_ref()) }
}
}
impl<T> PartialEq<T> for IdRef
where
T: Borrow<IdRef>,
{
fn eq(&self, other: &T) -> bool {
self == other.borrow()
}
}
impl<T> PartialOrd<T> for IdRef
where
T: Borrow<IdRef>,
{
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
self.partial_cmp(other.borrow())
}
}
pub unsafe fn parse_id<R, W>(readable: &mut R, writeable: &mut W) -> Result<usize, Error>
where
R: ?Sized + Read,
W: ?Sized + Write,
{
use crate::misc::{read_u8, write_u8};
let first = read_u8(readable)?;
write_u8(writeable, first)?;
if first & LONG_FLAG != LONG_FLAG {
return Ok(1);
}
let mut octet = read_u8(readable)?;
if octet <= MAX_SHORT {
return Err(Error::RedundantBytes);
}
if octet == MORE_FLAG {
return Err(Error::RedundantBytes);
}
for i in 2.. {
write_u8(writeable, octet)?;
if octet & MORE_FLAG != MORE_FLAG {
return Ok(i);
}
octet = read_u8(readable)?;
}
unreachable!();
}
pub unsafe fn parse_id_unchecked<'a>(bytes: &mut &'a [u8]) -> &'a IdRef {
if bytes[0] & LONG_FLAG != LONG_FLAG {
let parsed = &bytes[..1];
*bytes = &bytes[1..];
return IdRef::from_bytes_unchecked(parsed);
} else {
for i in 1.. {
if bytes[i] & MORE_FLAG != MORE_FLAG {
let parsed = &bytes[..=i];
*bytes = &bytes[i + 1..];
return IdRef::from_bytes_unchecked(parsed);
}
}
}
unreachable!()
}
#[cfg(test)]
mod tests {
use super::*;
const CLASSES: &[ClassTag] = &[
ClassTag::Universal,
ClassTag::Application,
ClassTag::ContextSpecific,
ClassTag::Private,
];
const PCS: &[PCTag] = &[PCTag::Primitive, PCTag::Constructed];
const MAX_2BYTES: u8 = 0x7f;
const MAX_3BYTES: u32 = 0x3fff;
#[test]
fn parse_1byte_ok() {
for &cl in CLASSES {
for &pc in PCS {
for i in 0..=MAX_SHORT {
let first = cl as u8 + pc as u8 + i;
let mut bytes: &[u8] = &[first];
let id = IdRef::parse(&mut bytes).unwrap();
assert_eq!(id.as_ref(), &[first]);
assert_eq!(bytes.len(), 0);
}
}
}
}
#[test]
fn parse_2byte_ok() {
for &cl in CLASSES {
for &pc in PCS {
for i in (MAX_SHORT + 1)..=MAX_2BYTES {
let first = cl as u8 + pc as u8 + LONG_FLAG;
let mut bytes: &[u8] = &[first, i];
let id = IdRef::parse(&mut bytes).unwrap();
assert_eq!(id.as_ref(), &[first, i]);
assert_eq!(bytes.len(), 0);
}
}
}
}
#[test]
fn parse_3byte_ok() {
for &cl in CLASSES {
for &pc in PCS {
for i in (MAX_2BYTES as u32 + 1)..=MAX_3BYTES {
let first = cl as u8 + pc as u8 + LONG_FLAG;
let j = (i >> 7) as u8 | MORE_FLAG;
let k = i as u8 & !MORE_FLAG;
let mut bytes: &[u8] = &[first, j, k];
let id = IdRef::parse(&mut bytes).unwrap();
assert_eq!(id.as_ref(), &[first, j, k]);
assert_eq!(bytes.len(), 0);
}
}
}
}
#[test]
fn parse_4byte_ok() {
for &cl in CLASSES {
for &pc in PCS {
let i = MAX_3BYTES + 1;
let first = cl as u8 + pc as u8 + LONG_FLAG;
let j = (i >> 14) as u8 | MORE_FLAG;
let k = (i >> 7) as u8 | MORE_FLAG;
let l = i as u8 & !MORE_FLAG;
let mut bytes: &[u8] = &[first, j, k, l];
let id = IdRef::parse(&mut bytes).unwrap();
assert_eq!(id.as_ref(), &[first, j, k, l]);
assert_eq!(bytes.len(), 0);
}
}
}
#[test]
fn parse_2byte_redundant() {
for &cl in CLASSES {
for &pc in PCS {
for i in 0..=MAX_SHORT {
let first = cl as u8 + pc as u8 + LONG_FLAG;
let mut bytes: &[u8] = &[first, i];
let e = IdRef::parse(&mut bytes);
assert_eq!(Err(Error::RedundantBytes), e);
assert_eq!(bytes, &[first, i]);
}
}
}
}
#[test]
fn parse_3byte_redundant() {
for &cl in CLASSES {
for &pc in PCS {
for i in 0..=u8::MAX {
let first = cl as u8 + pc as u8 + LONG_FLAG;
let mut bytes: &[u8] = &[first, 0x00, i];
let e = IdRef::parse(&mut bytes);
assert_eq!(Err(Error::RedundantBytes), e);
assert_eq!(bytes, &[first, 0x00, i]);
}
}
}
}
#[test]
fn parse_empty_unterminated() {
let mut bytes: &[u8] = &[];
let e = IdRef::parse(&mut bytes);
assert_eq!(Err(Error::UnterminatedBytes), e);
assert_eq!(bytes, &[]);
}
#[test]
fn parse_1byte_unterminated() {
for &cl in CLASSES {
for &pc in PCS {
let first = cl as u8 + pc as u8 + LONG_FLAG;
let mut bytes: &[u8] = &[first];
let e = IdRef::parse(&mut bytes);
assert_eq!(Err(Error::UnterminatedBytes), e);
assert_eq!(bytes, &[first]);
}
}
}
#[test]
fn parse_2byte_unterminated() {
for &cl in CLASSES {
for &pc in PCS {
for i in (MORE_FLAG + 1)..=u8::MAX {
let first = cl as u8 + pc as u8 + LONG_FLAG;
let mut bytes: &[u8] = &[first, i];
let e = IdRef::parse(&mut bytes);
assert_eq!(Err(Error::UnterminatedBytes), e);
assert_eq!(bytes, &[first, i]);
}
}
}
}
#[test]
fn parse_3byte_unterminated() {
for &cl in CLASSES {
for &pc in PCS {
for i in (MORE_FLAG + 1)..=u8::MAX {
for j in MORE_FLAG..=u8::MAX {
let first = cl as u8 + pc as u8 + LONG_FLAG;
let mut bytes: &[u8] = &[first, i, j];
let e = IdRef::parse(&mut bytes);
assert_eq!(Err(Error::UnterminatedBytes), e);
assert_eq!(bytes, &[first, i, j]);
}
}
}
}
}
#[test]
fn set_class() {
for &cl0 in CLASSES {
for &pc in PCS {
for &cl1 in CLASSES {
for i in 0..=u16::MAX {
let mut id = Id::new(cl0, pc, i.into());
id.set_class(cl1);
assert_eq!(cl1, id.class());
assert_eq!(pc, id.pc());
assert_eq!(Ok(i.into()), id.number());
}
}
}
}
}
#[test]
fn set_pc() {
for &cl in CLASSES {
for &pc0 in PCS {
for &pc1 in PCS {
for i in 0..=u16::MAX {
let mut id = Id::new(cl, pc0, i.into());
id.set_pc(pc1);
assert_eq!(cl, id.class());
assert_eq!(pc1, id.pc());
assert_eq!(Ok(i.into()), id.number());
}
}
}
}
}
}