//! Benchmarking the decoding of constrained octet strings
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rasn::prelude::*;
use rasn::{ber, oer, uper};

#[derive(AsnType, Decode, Encode, Debug, Clone, PartialEq)]
#[rasn(automatic_tags)]
pub struct Hashes {
    #[rasn(size("3"))]
    pub hashed3: OctetString,
    #[rasn(size("8"))]
    pub hashed8: OctetString,
    #[rasn(size("16"))]
    pub hashed16: OctetString,
    #[rasn(size("32"))]
    pub hashed32: OctetString,
    #[rasn(size("64"))]
    pub hashed64: OctetString,
}
#[derive(AsnType, Decode, Encode, Debug, Clone, PartialEq)]
#[rasn(automatic_tags)]
pub struct ConstrainedHashes {
    #[rasn(size("3"))]
    pub hashed3: FixedOctetString<3>,
    #[rasn(size("8"))]
    pub hashed8: FixedOctetString<8>,
    #[rasn(size("16"))]
    pub hashed16: FixedOctetString<16>,
    #[rasn(size("32"))]
    pub hashed32: FixedOctetString<32>,
    #[rasn(size("64"))]
    pub hashed64: FixedOctetString<64>,
}

fn build_octet() -> Hashes {
    Hashes {
        hashed3: vec![0x01, 0x02, 0x03].into(),
        hashed8: vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08].into(),
        hashed16: vec![
            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
            0x0F, 0x10,
        ]
        .into(),
        hashed32: vec![
            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
            0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
            0x1D, 0x1E, 0x1F, 0x20,
        ]
        .into(),
        hashed64: vec![
            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
            0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
            0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
            0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
            0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40,
        ]
        .into(),
    }
}
fn build_fixed_octet() -> ConstrainedHashes {
    ConstrainedHashes {
        hashed3: [0x01, 0x02, 0x03].into(),
        hashed8: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08].into(),
        hashed16: [
            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
            0x0F, 0x10,
        ]
        .into(),
        hashed32: [
            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
            0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
            0x1D, 0x1E, 0x1F, 0x20,
        ]
        .into(),
        hashed64: [
            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
            0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
            0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
            0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
            0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40,
        ]
        .into(),
    }
}

macro_rules! rasn_dec_octet_fn {
    ($fn_name:ident, $codec:ident) => {
        fn $fn_name(c: &mut Criterion) {
            let items = build_octet();
            let encoded = $codec::encode(&items).unwrap();
            c.bench_function(
                &format!(
                    "RASN/decode {} - octet string",
                    stringify!($codec).to_uppercase()
                ),
                |b| b.iter(|| black_box($codec::decode::<Hashes>(&encoded).unwrap())),
            );
        }
    };
}

macro_rules! rasn_dec_fixed_octet_fn {
    ($fn_name:ident, $codec:ident) => {
        fn $fn_name(c: &mut Criterion) {
            let items = build_fixed_octet();
            let encoded = $codec::encode(&items).unwrap();
            c.bench_function(
                &format!(
                    "RASN/decode {} - fixed-size octet string",
                    stringify!($codec).to_uppercase()
                ),
                |b| b.iter(|| black_box($codec::decode::<ConstrainedHashes>(&encoded).unwrap())),
            );
        }
    };
}

rasn_dec_octet_fn!(uper_octet, uper);
rasn_dec_octet_fn!(oer_octet, oer);
rasn_dec_octet_fn!(ber_octet, ber);

rasn_dec_fixed_octet_fn!(uper_fixed_octet, uper);
rasn_dec_fixed_octet_fn!(oer_fixed_octet, oer);
rasn_dec_fixed_octet_fn!(ber_fixed_octet, ber);

criterion_group!(
    benches,
    uper_octet,
    uper_fixed_octet,
    oer_octet,
    oer_fixed_octet,
    ber_octet,
    ber_fixed_octet
);
criterion_main!(benches);
