use super::{Element, ScopedReader};
use crate::{Error, ErrorCode, Integer, Limit, Result, value};
#[derive(Debug)]
pub struct IntegerReading<'a> {
original: &'a [u8],
value: Integer<'a>,
fault: Option<Error>,
}
impl<'a> IntegerReading<'a> {
#[must_use]
pub fn as_bytes(&self) -> &'a [u8] {
self.original
}
#[must_use]
pub fn value(&self) -> Integer<'a> {
self.value
}
#[must_use]
pub fn fault(&self) -> Option<&Error> {
self.fault.as_ref()
}
#[must_use]
pub fn into_parts(self) -> (Integer<'a>, Option<Error>) {
(self.value, self.fault)
}
}
impl<'a: 'operation, 'operation> ScopedReader<'a, 'operation> {
pub fn integer_reading(&self, element: &Element<'a>) -> Result<IntegerReading<'a>> {
self.primitive(element)?;
self.budget()
.work_in(3, self.memory())
.map_err(|limit| self.fail_under(ErrorCode::LimitExceeded(limit), element))?;
let original = element.contents();
match value::integer(original) {
Ok(value) => Ok(IntegerReading {
original,
value,
fault: None,
}),
Err(ErrorCode::IntegerNotMinimal) => {
let work = u64::try_from(original.len())
.ok()
.and_then(|n| n.checked_mul(2))
.and_then(|n| n.checked_add(1))
.ok_or_else(|| {
self.fail_under(ErrorCode::LimitExceeded(Limit::Work), element)
})?;
self.budget()
.work_in(work, self.memory())
.map_err(|limit| self.fail_under(ErrorCode::LimitExceeded(limit), element))?;
let value = value::recovered_integer(original);
let fault = self.fail_under(ErrorCode::IntegerNotMinimal, element);
if fault.is_resource() {
return Err(fault);
}
Ok(IntegerReading {
original,
value,
fault: Some(fault),
})
}
Err(code) => Err(self.fail_under(code, element)),
}
}
}