use std::io;
use bytes::Bytes;
use crate::length::Length;
use crate::mode::Mode;
use crate::tag::Tag;
use super::values::Values;
pub trait PrimitiveContent: Sized {
const TAG: Tag;
fn encoded_len(&self, mode: Mode) -> usize;
fn write_encoded<W: io::Write>(
&self,
mode: Mode,
target: &mut W
) -> Result<(), io::Error>;
fn to_encoded_bytes(&self, mode: Mode) -> Bytes {
let l = self.encoded_len(mode);
let mut w = Vec::with_capacity(l);
self.write_encoded(mode, &mut w).unwrap();
w.into()
}
fn encode(self) -> Primitive<Self> {
self.encode_as(Self::TAG)
}
fn encode_as(self, tag: Tag) -> Primitive<Self> {
Primitive { tag, prim: self }
}
fn encode_ref(&self) -> Primitive<&Self> {
self.encode_ref_as(Self::TAG)
}
fn encode_ref_as(&self, tag: Tag) -> Primitive<&Self> {
Primitive { tag, prim: self }
}
}
impl<T: PrimitiveContent> PrimitiveContent for &'_ T {
const TAG: Tag = T::TAG;
fn encoded_len(&self, mode: Mode) -> usize {
(*self).encoded_len(mode)
}
fn write_encoded<W: io::Write>(
&self,
mode: Mode,
target: &mut W
) -> Result<(), io::Error> {
(*self).write_encoded(mode, target)
}
}
impl PrimitiveContent for u8 {
const TAG: Tag = Tag::INTEGER;
fn encoded_len(&self, _: Mode) -> usize {
if *self > 0x7F { 2 }
else { 1 }
}
fn write_encoded<W: io::Write>(
&self,
_: Mode,
target: &mut W
) -> Result<(), io::Error> {
if *self > 0x7F {
target.write_all(&[0])?;
}
target.write_all(&[*self])?;
Ok(())
}
}
macro_rules! unsigned_content {
( $type:ident, $len:expr) => {
impl PrimitiveContent for $type {
const TAG: Tag = Tag::INTEGER;
fn encoded_len(&self, _: Mode) -> usize {
if *self == 0 {
1
}
else {
let zeros = self.leading_zeros() as usize;
if zeros % 8 == 0 {
$len - (zeros >> 3) + 1
}
else {
$len - (zeros >> 3)
}
}
}
fn write_encoded<W: io::Write>(
&self,
_: Mode,
target: &mut W
) -> Result<(), io::Error> {
if *self == 0 {
target.write_all(&[0x00])?;
}
else {
let mut val = self.swap_bytes();
let mut i = 0;
while i < $len {
if val as u8 != 0 {
break
}
val >>= 8;
i += 1
}
if val & 0x80 != 0 {
target.write_all(&[0x00])?;
}
while i < $len {
target.write_all(&[val as u8])?;
val >>= 8;
i += 1
}
}
Ok(())
}
}
}
}
unsigned_content!(u16, 2);
unsigned_content!(u32, 4);
unsigned_content!(u64, 8);
unsigned_content!(u128, 16);
impl PrimitiveContent for i8 {
const TAG: Tag = Tag::INTEGER;
fn encoded_len(&self, _: Mode) -> usize {
1
}
fn write_encoded<W: io::Write>(
&self,
_: Mode,
target: &mut W
) -> Result<(), io::Error> {
target.write_all(&[*self as u8])?;
Ok(())
}
}
macro_rules! signed_content {
($type:ident, $len:expr) => {
impl PrimitiveContent for $type {
const TAG: Tag = Tag::INTEGER;
#[allow(clippy::verbose_bit_mask)]
fn encoded_len(&self, _: Mode) -> usize {
if *self == 0 || *self == -1 {
return 1
}
let zeros = if *self < 0 {
(!*self).leading_zeros() as usize
}
else {
self.leading_zeros() as usize
};
if zeros & 0x07 == 0 { $len + 1 - (zeros >> 3)
}
else {
$len - (zeros >> 3)
}
}
fn write_encoded<W: io::Write>(
&self,
_: Mode,
target: &mut W
) -> Result<(), io::Error> {
if *self == 0 {
target.write_all(&[0x00])?;
}
else if *self == -1 {
target.write_all(&[0xFF])?;
}
else if *self < 0 {
let mut val = self.swap_bytes();
let mut i = 0;
while i < $len {
if val as u8 != 0xFF {
break
}
val >>= 8;
i += 1;
}
if val & 0x80 != 0x80 {
target.write_all(&[0xFF])?;
}
while i < $len {
target.write_all(&[val as u8])?;
val >>= 8;
i += 1
}
}
else {
let mut val = self.swap_bytes();
let mut i = 0;
while i < $len {
if val as u8 != 0x00 {
break
}
val >>= 8;
i += 1;
}
if val & 0x80 == 0x80 {
target.write_all(&[0x00])?;
}
while i < $len {
target.write_all(&[val as u8])?;
val >>= 8;
i += 1
}
}
Ok(())
}
}
}
}
signed_content!(i16, 2);
signed_content!(i32, 4);
signed_content!(i64, 8);
signed_content!(i128, 16);
impl PrimitiveContent for () {
const TAG: Tag = Tag::NULL;
fn encoded_len(&self, _: Mode) -> usize {
0
}
fn write_encoded<W: io::Write>(
&self,
_: Mode,
_: &mut W
) -> Result<(), io::Error> {
Ok(())
}
}
impl PrimitiveContent for bool {
const TAG: Tag = Tag::BOOLEAN;
fn encoded_len(&self, _: Mode) -> usize {
1
}
fn write_encoded<W: io::Write>(
&self,
_: Mode,
target: &mut W
) -> Result<(), io::Error> {
if *self {
target.write_all(&[0xff])
}
else {
target.write_all(&[0])
}
}
}
impl PrimitiveContent for &'_ [u8] {
const TAG: Tag = Tag::OCTET_STRING;
fn encoded_len(&self, _: Mode) -> usize {
self.len()
}
fn write_encoded<W: io::Write>(
&self,
_: Mode,
target: &mut W
) -> Result<(), io::Error> {
target.write_all(self)
}
}
pub struct Primitive<P> {
tag: Tag,
prim: P
}
impl<P: PrimitiveContent> Values for Primitive<P> {
fn encoded_len(&self, mode: Mode) -> usize {
let len = self.prim.encoded_len(mode);
self.tag.encoded_len()
+ Length::Definite(len).encoded_len()
+ len
}
fn write_encoded<W: io::Write>(
&self,
mode: Mode,
target: &mut W
) -> Result<(), io::Error> {
self.tag.write_encoded(false, target)?;
Length::Definite(self.prim.encoded_len(mode)).write_encoded(target)?;
self.prim.write_encoded(mode, target)
}
}
#[cfg(test)]
mod test {
use super::*;
fn test_der<T: PrimitiveContent>(value: T, expected: &[u8]) {
assert_eq!(value.encoded_len(Mode::Der), expected.len());
let mut target = Vec::new();
value.write_encoded(Mode::Der, &mut target).unwrap();
assert_eq!(target, expected);
}
#[test]
fn encode_u32() {
test_der(0u32, b"\0");
test_der(0x12u32, b"\x12");
test_der(0xf2u32, b"\0\xf2");
test_der(0x1234u32, b"\x12\x34");
test_der(0xf234u32, b"\0\xf2\x34");
test_der(0x123400u32, b"\x12\x34\x00");
test_der(0xf23400u32, b"\0\xf2\x34\x00");
test_der(0x12345678u32, b"\x12\x34\x56\x78");
test_der(0xA2345678u32, b"\x00\xA2\x34\x56\x78");
}
#[test]
fn encode_i32() {
test_der(0i32, b"\0");
test_der(0x12i32, b"\x12");
test_der(0xf2i32, b"\0\xf2");
test_der(0x1234i32, b"\x12\x34");
test_der(0xf234i32, b"\0\xf2\x34");
test_der(0x123400i32, b"\x12\x34\x00");
test_der(0xf23400i32, b"\0\xf2\x34\x00");
test_der(0x12345678i32, b"\x12\x34\x56\x78");
test_der(-1i32, b"\xFF");
test_der(-0xF0i32, b"\xFF\x10");
test_der(-0xF000i32, b"\xFF\x10\x00");
test_der(-12000i32, b"\xD1\x20");
test_der(-1200000i32, b"\xED\xB0\x80");
}
}