use std::sync::Arc;
use structured_asn1::memory::{Memory, MemoryAccount};
use structured_asn1::{Encoding, ErrorCode, Limits, Parser};
fn memory(storage: usize, work: usize) -> Memory {
Memory::new(Arc::new(MemoryAccount::new(storage, work)))
}
#[test]
fn recovery_keeps_original_bytes_and_the_unique_signed_value() {
for (raw, minimal, unsigned, signed) in [
(&[0][..], &[0][..], Some(0), Some(0)),
(&[0, 0, 1], &[1], Some(1), Some(1)),
(&[0, 0, 0x80], &[0, 0x80], Some(128), Some(128)),
(&[0xff, 0xff], &[0xff], None, Some(-1)),
(&[0xff, 0xff, 0x80], &[0x80], None, Some(-128)),
(&[0xff, 0xff, 0x7f], &[0xff, 0x7f], None, Some(-129)),
(&[0, 0, 0], &[0], Some(0), Some(0)),
] {
let bytes = [&[2, u8::try_from(raw.len()).unwrap()][..], raw].concat();
let parser = Parser::der(memory(65536, 1_000_000));
let mut reader = parser.read(&bytes, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
let reading = reader.integer_reading(&element).unwrap();
assert_eq!(reading.as_bytes(), raw);
assert_eq!(reading.as_bytes().as_ptr(), element.contents().as_ptr());
assert_eq!(reading.value().as_bytes(), minimal);
assert_eq!(
reading.value().as_bytes().as_ptr(),
element.contents()[raw.len() - minimal.len()..].as_ptr()
);
assert_eq!(reading.value().as_u64(), unsigned);
assert_eq!(reading.value().as_i64(), signed);
assert_eq!(reading.value().is_negative(), signed.unwrap() < 0);
if raw == minimal {
assert!(reading.fault().is_none());
} else {
let fault = reading.fault().unwrap();
assert_eq!(fault.code(), ErrorCode::IntegerNotMinimal);
assert_eq!(fault.offset(), 400);
assert_eq!(fault.path().indices(), &[0]);
assert_eq!(
reader.integer(&element).unwrap_err().code(),
ErrorCode::IntegerNotMinimal
);
}
}
}
#[test]
fn implicit_integer_and_numeric_width_remain_separate_from_faults() {
for (raw, unsigned, signed) in [
(
&[0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..],
Some(u64::MAX),
None,
),
(&[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], None, None),
(&[0xff, 0x80, 0, 0, 0, 0, 0, 0, 0], None, Some(i64::MIN)),
] {
let bytes = [&[0x80, u8::try_from(raw.len()).unwrap()][..], raw].concat();
let parser = Parser::ber(memory(65536, 1_000_000));
let mut reader = parser.read(&bytes, 900).unwrap();
let element = reader.next_element().unwrap().unwrap();
let reading = reader.integer_reading(&element).unwrap();
assert!(reading.fault().is_some());
assert_eq!(reading.value().as_u64(), unsigned);
assert_eq!(reading.value().as_i64(), signed);
}
}
#[test]
fn empty_and_constructed_integers_are_not_recovered() {
for (bytes, code) in [
(&[2, 0][..], ErrorCode::IntegerEmpty),
(&[0x22, 3, 2, 1, 1], ErrorCode::WrongConstruction),
] {
let parser = Parser::der(memory(65536, 1_000_000));
let mut reader = parser.read(bytes, 700).unwrap();
let element = reader.next_element().unwrap().unwrap();
let error = reader.integer_reading(&element).unwrap_err();
assert_eq!(error.code(), code);
assert_eq!(error.offset(), 700);
}
}
#[test]
fn recovery_diagnostic_owns_its_charge_and_moves_without_copying() {
let execution = memory(65536, 1_000_000);
let fault = {
let parser = Parser::der(execution.clone());
let mut reader = parser.read(&[2, 2, 0, 1], 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
let reading = reader.integer_reading(&element).unwrap();
let retained = execution.used();
assert!(retained > 0);
let (value, fault) = reading.into_parts();
assert_eq!(value.as_u64(), Some(1));
assert_eq!(execution.used(), retained);
fault.unwrap()
};
assert!(execution.used() > 0);
assert_eq!(fault.offset(), 400);
drop(fault);
assert_eq!(execution.used(), 0);
}
#[test]
fn unfunded_diagnostics_cannot_be_successful_recovery() {
let execution = memory(0, 1_000_000);
let parser = Parser::der(execution.clone());
for (bytes, minimal) in [(&[2, 1, 1][..], true), (&[2, 2, 0, 1], false)] {
let mut reader = parser.read(bytes, 800).unwrap();
let element = reader.next_element().unwrap().unwrap();
let result = reader.integer_reading(&element);
if minimal {
assert_eq!(result.unwrap().value().as_u64(), Some(1));
} else {
let error = result.unwrap_err();
assert!(error.is_resource());
assert_eq!(error.offset(), 800);
}
assert_eq!(execution.used(), 0);
}
}
#[test]
fn rebound_recovery_preserves_local_and_current_work_ceilings() {
for local in [false, true] {
let source = memory(65536, 1_000_000);
let limits = Limits {
max_work: if local { 2 } else { 1_000_000 },
..Limits::default()
};
let parser = Parser::new(Encoding::Der, limits, source.clone());
let mut reader = parser.read(&[2, 4, 0, 0, 0, 1], 800).unwrap();
let element = reader.next_element().unwrap().unwrap();
let source_work = source.work_used();
let execution = memory(65536, if local { 1_000_000 } else { 0 });
let reader = reader.with_execution(&execution);
let error = reader.integer_reading(&element).unwrap_err();
assert!(error.is_resource());
assert_eq!(error.offset(), 800);
assert_eq!(source.work_used(), source_work);
if !local {
assert_eq!(execution.work_used(), 0);
}
}
}
#[test]
fn long_padding_is_iterative_borrowed_and_not_rescanned_by_conversion() {
let mut bytes = vec![2, 0x83, 1, 0, 1];
bytes.resize(5 + 65536, 0);
bytes.push(1);
let execution = memory(1024, 1_000_000);
let parser = Parser::der(execution.clone());
let mut reader = parser.read(&bytes, 400).unwrap();
let element = reader.next_element().unwrap().unwrap();
let reading = reader.integer_reading(&element).unwrap();
assert_eq!(reading.as_bytes().len(), 65537);
assert_eq!(reading.value().as_bytes(), &[1]);
assert_eq!(
reading.value().as_bytes().as_ptr(),
bytes[bytes.len() - 1..].as_ptr()
);
assert!(execution.used() <= 1024);
let work = execution.work_used();
for _ in 0..1000 {
assert_eq!(reading.value().as_u64(), Some(1));
assert_eq!(reading.value().as_i64(), Some(1));
}
assert_eq!(execution.work_used(), work);
drop(reading);
assert_eq!(execution.used(), 0);
}
#[test]
fn nested_fault_keeps_its_original_container_path() {
let parser = Parser::der(memory(65536, 1_000_000));
let mut reader = parser.read(&[48, 4, 0x81, 2, 0, 1], 900).unwrap();
let root = reader.next_element().unwrap().unwrap();
let mut inside = reader.enter(&root).unwrap();
let element = inside.next_element().unwrap().unwrap();
let reading = inside.integer_reading(&element).unwrap();
let fault = reading.fault().unwrap();
assert_eq!(fault.offset(), 902);
assert_eq!(fault.path().indices(), &[0, 0]);
assert_eq!(reading.value().as_u32(), Some(1));
}
#[test]
fn nested_diagnostic_refusal_rolls_back_partial_path_storage() {
let mut bytes = vec![2, 2, 0, 1];
for _ in 0..8 {
bytes = [&[48, u8::try_from(bytes.len()).unwrap()][..], &bytes].concat();
}
let source = memory(65536, 1_000_000);
let parser = Parser::der(source.clone());
let mut reader = parser.read(&bytes, 400).unwrap();
for _ in 0..8 {
let element = reader.next_element().unwrap().unwrap();
reader = reader.enter(&element).unwrap();
}
let element = reader.next_element().unwrap().unwrap();
let retained = source.used();
let mut refused = false;
let mut admitted = false;
for storage in 0..=128 {
let execution = memory(storage, 1_000_000);
reader = reader.with_execution(&execution);
match reader.integer_reading(&element) {
Ok(reading) => {
admitted = true;
assert_eq!(reading.fault().unwrap().path().indices(), &[0; 9]);
}
Err(error) => {
refused = true;
assert!(error.is_resource());
assert_eq!(error.offset(), 416);
}
}
assert_eq!(execution.used(), 0);
assert_eq!(source.used(), retained);
}
assert!(refused && admitted);
}