use crate::{identifier_ref, length, Ber, ContentsRef, DerRef, Error, IdRef, Length};
use std::borrow::Borrow;
use std::mem;
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct BerRef {
bytes: [u8],
}
impl BerRef {
pub const fn eoc() -> &'static Self {
const BYTES: [u8; 2] = [0x00, 0x00];
unsafe { Self::from_bytes_unchecked(&BYTES) }
}
pub fn parse<'a>(bytes: &mut &'a [u8]) -> Result<&'a Self, Error> {
let init_bytes = *bytes;
let mut stack_depth: isize = 0;
while {
stack_depth += Self::do_parse(bytes)? as isize;
stack_depth > 0
} {}
let total_len = init_bytes.len() - bytes.len();
unsafe { Ok(Self::from_bytes_unchecked(&init_bytes[..total_len])) }
}
pub fn parse_mut<'a>(bytes: &mut &'a mut [u8]) -> Result<&'a mut Self, Error> {
let read_bytes = {
let mut readable: &[u8] = *bytes;
let mut stack_depth: isize = 0;
while {
stack_depth += Self::do_parse(&mut readable)? as isize;
stack_depth > 0
} {}
bytes.len() - readable.len()
};
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))
}
}
fn do_parse(readable: &mut &[u8]) -> Result<i8, Error> {
if readable.starts_with(Self::eoc().as_ref()) {
*readable = &readable[Self::eoc().as_ref().len()..];
return Ok(-1);
}
let mut writeable = std::io::sink();
match unsafe { crate::misc::parse_id_length(readable, &mut writeable)? } {
Length::Definite(contents_len) => {
if readable.len() < contents_len {
Err(Error::UnterminatedBytes)
} else {
*readable = &readable[contents_len..];
Ok(0)
}
}
Length::Indefinite => Ok(1),
}
}
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 fn id(&self) -> &IdRef {
unsafe { identifier_ref::parse_id_unchecked(&mut &self.bytes) }
}
pub fn mut_id(&mut self) -> &mut IdRef {
self.disassemble_mut().0
}
pub fn length(&self) -> Length {
self.disassemble().1
}
pub fn contents(&self) -> &ContentsRef {
self.disassemble().2
}
pub fn mut_contents(&mut self) -> &mut ContentsRef {
self.disassemble_mut().2
}
pub fn disassemble(&self) -> (&IdRef, Length, &ContentsRef) {
let mut bytes = &self.bytes;
let id = unsafe { identifier_ref::parse_id_unchecked(&mut bytes) };
let length = unsafe { length::parse_length_unchecked(&mut bytes) };
let contents = bytes.into();
(id, length, contents)
}
pub fn disassemble_mut(&mut self) -> (&mut IdRef, Length, &mut ContentsRef) {
let (id, length, contents) = self.disassemble();
let id_ptr = id as *const IdRef;
let id_ptr = id_ptr as *mut IdRef;
let contents_ptr = contents as *const ContentsRef;
let contents_ptr = contents_ptr as *mut ContentsRef;
unsafe { (&mut *id_ptr, length, &mut *contents_ptr) }
}
}
impl<'a> From<&'a DerRef> for &'a BerRef {
fn from(der: &'a DerRef) -> Self {
unsafe { BerRef::from_bytes_unchecked(der.as_ref()) }
}
}
impl AsRef<[u8]> for BerRef {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl ToOwned for BerRef {
type Owned = Ber;
fn to_owned(&self) -> Self::Owned {
unsafe { Ber::from_bytes_unchecked(self.as_ref()) }
}
}
impl<T> PartialEq<T> for BerRef
where
T: Borrow<BerRef>,
{
fn eq(&self, other: &T) -> bool {
self == other.borrow()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_deinite() {
let bytes: Vec<u8> = (0..=u8::MAX).collect();
for i in 0..bytes.len() {
let ber = Ber::from(&bytes[..i]);
let mut bytes: &[u8] = ber.as_ref();
let parsed = BerRef::parse(&mut bytes).unwrap();
assert_eq!(ber, parsed);
assert_eq!(bytes.len(), 0);
}
}
#[test]
fn parse_indefinite() {
let bers: Vec<Ber> = (0..10).map(Ber::from).collect();
for i in 0..10 {
let contents: Vec<u8> = bers[..i]
.iter()
.map(|ber| ber.as_ref() as &[u8])
.flatten()
.copied()
.collect();
let mut ber =
unsafe { Ber::new_indefinite(IdRef::sequence(), contents.as_slice().into()) };
ber.extend_from_slice(BerRef::eoc());
let mut bytes: &[u8] = ber.as_ref();
let parsed = BerRef::parse(&mut bytes).unwrap();
assert_eq!(ber, parsed);
assert_eq!(bytes.len(), 0);
}
}
#[test]
fn parse_deinite_in_definite() {
let bytes: Vec<u8> = (0..=u8::MAX).collect();
for i in 0..bytes.len() {
let inner = Ber::from(&bytes[..i]);
let ber = Ber::new(IdRef::sequence(), (inner.as_ref() as &[u8]).into());
let mut bytes: &[u8] = ber.as_ref();
let parsed = BerRef::parse(&mut bytes).unwrap();
assert_eq!(ber, parsed);
assert_eq!(bytes.len(), 0);
}
}
#[test]
fn parse_indeinite_in_definite() {
let bers: Vec<Ber> = (0..10).map(Ber::from).collect();
for i in 0..bers.len() {
let contents: Vec<u8> = bers[..i]
.iter()
.map(|ber| ber.as_ref() as &[u8])
.flatten()
.copied()
.collect();
let mut inner =
unsafe { Ber::new_indefinite(IdRef::octet_string(), contents.as_slice().into()) };
inner.extend_from_slice(BerRef::eoc());
let ber = Ber::new(IdRef::sequence(), (inner.as_ref() as &[u8]).into());
let mut bytes: &[u8] = ber.as_ref();
let parsed = BerRef::parse(&mut bytes).unwrap();
assert_eq!(ber, parsed);
assert_eq!(bytes.len(), 0);
}
}
#[test]
fn parse_deinite_in_indefinite() {
let bytes: Vec<u8> = (0..=u8::MAX).collect();
for i in 0..bytes.len() {
let inner = Ber::from(&bytes[..i]);
let mut ber =
unsafe { Ber::new_indefinite(IdRef::sequence(), (inner.as_ref() as &[u8]).into()) };
ber.extend_from_slice(BerRef::eoc());
let mut bytes: &[u8] = ber.as_ref();
let parsed = BerRef::parse(&mut bytes).unwrap();
assert_eq!(ber, parsed);
assert_eq!(bytes.len(), 0);
}
}
#[test]
fn parse_indeinite_in_indefinite() {
let bers: Vec<Ber> = (0..10).map(Ber::from).collect();
for i in 0..bers.len() {
let contents: Vec<u8> = bers[..i]
.iter()
.map(|ber| ber.as_ref() as &[u8])
.flatten()
.copied()
.collect();
let mut inner =
unsafe { Ber::new_indefinite(IdRef::octet_string(), contents.as_slice().into()) };
inner.extend_from_slice(BerRef::eoc());
let mut ber =
unsafe { Ber::new_indefinite(IdRef::sequence(), (inner.as_ref() as &[u8]).into()) };
ber.extend_from_slice(BerRef::eoc());
let mut bytes: &[u8] = ber.as_ref();
let parsed = BerRef::parse(&mut bytes).unwrap();
assert_eq!(ber, parsed);
assert_eq!(bytes.len(), 0);
}
}
}