use crate::{Contents, Error};
use num::PrimInt;
use std::borrow::{Borrow, ToOwned};
use std::mem;
use std::mem::MaybeUninit;
use std::ops::{Index, IndexMut};
use std::slice::SliceIndex;
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ContentsRef {
bytes: [u8],
}
impl ContentsRef {
pub const fn len(&self) -> usize {
self.bytes.len()
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn to_integer<T>(&self) -> Result<T, Error>
where
T: PrimInt,
{
if self.is_empty() {
return Err(Error::UnterminatedBytes);
}
if 1 < self.len() {
if (self[0] == 0) && (self[1] & 0x80 == 0x00) {
return Err(Error::RedundantBytes);
}
if (self[0] == 0xff) && (self[1] & 0x80 == 0x80) {
return Err(Error::RedundantBytes);
}
}
let bytes = if self[0] == 0x00 {
&self[1..]
} else {
self.as_ref()
};
if mem::size_of::<T>() < bytes.len() {
return Err(Error::OverFlow);
}
let v = unsafe { self.to_integer_unchecked() };
if self[0] & 0x80 == 0x00 {
if v < T::zero() {
return Err(Error::OverFlow);
}
} else {
if T::zero() <= v {
return Err(Error::OverFlow);
}
}
Ok(v)
}
pub unsafe fn to_integer_unchecked<T>(&self) -> T
where
T: PrimInt,
{
let bytes = if self[0] == 0x00 {
&self[1..]
} else {
self.as_ref()
};
let filler = if self[0] & 0x80 == 0x00 { 0x00 } else { 0xff };
let mut be: MaybeUninit<T> = MaybeUninit::uninit();
be.as_mut_ptr().write_bytes(filler, 1);
let dst = be.as_mut_ptr() as *mut u8;
let dst = dst.add(mem::size_of::<T>() - bytes.len());
dst.copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
T::from_be(be.assume_init())
}
pub const fn to_bool_ber(&self) -> Result<bool, Error> {
if self.is_empty() {
Err(Error::UnterminatedBytes)
} else if 1 < self.len() {
Err(Error::ExtraContentsOctet)
} else if self.bytes[0] == 0x00 {
Ok(false)
} else {
Ok(true)
}
}
pub const fn to_bool_der(&self) -> Result<bool, Error> {
if self.is_empty() {
Err(Error::UnterminatedBytes)
} else if 1 < self.len() {
Err(Error::ExtraContentsOctet)
} else {
match self.bytes[0] {
0x00 => Ok(false),
0xff => Ok(true),
_ => Err(Error::InvalidDerBooleanContents),
}
}
}
}
impl<'a> From<&'a [u8]> for &'a ContentsRef {
fn from(bytes: &'a [u8]) -> Self {
unsafe { mem::transmute(bytes) }
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for &'a ContentsRef {
fn from(bytes: &'a [u8; N]) -> Self {
Self::from(&bytes[..])
}
}
impl<'a> From<&'a mut [u8]> for &'a mut ContentsRef {
fn from(bytes: &'a mut [u8]) -> Self {
unsafe { mem::transmute(bytes) }
}
}
impl<'a, const N: usize> From<&'a mut [u8; N]> for &'a mut ContentsRef {
fn from(bytes: &'a mut [u8; N]) -> Self {
Self::from(&mut bytes[..])
}
}
impl<'a> From<&'a str> for &'a ContentsRef {
fn from(s: &'a str) -> Self {
Self::from(s.as_bytes())
}
}
impl From<bool> for &'static ContentsRef {
fn from(val: bool) -> Self {
if val {
Self::from(&[0xff])
} else {
Self::from(&[0x00])
}
}
}
impl AsRef<[u8]> for ContentsRef {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl AsMut<[u8]> for ContentsRef {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.bytes
}
}
impl<T> Index<T> for ContentsRef
where
T: SliceIndex<[u8]>,
{
type Output = T::Output;
fn index(&self, index: T) -> &Self::Output {
&self.as_ref()[index]
}
}
impl<T> IndexMut<T> for ContentsRef
where
T: SliceIndex<[u8]>,
{
fn index_mut(&mut self, index: T) -> &mut Self::Output {
&mut self.bytes[index]
}
}
impl<T> PartialEq<T> for ContentsRef
where
T: Borrow<ContentsRef>,
{
fn eq(&self, other: &T) -> bool {
self.as_ref() == other.borrow().as_ref()
}
}
impl ToOwned for ContentsRef {
type Owned = Contents;
fn to_owned(&self) -> Self::Owned {
Contents::from(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_bool() {
{
let contents = <&ContentsRef>::from(true);
assert_eq!(&[0xff], contents.as_ref());
}
{
let contents = <&ContentsRef>::from(false);
assert_eq!(&[0x00], contents.as_ref());
}
}
}