use monty_types::{ExcType, LARGE_RESULT_THRESHOLD, ResourceError, ResourceTracker};
use crate::exception_private::{RunError, SimpleException};
pub fn check_repeat_size(item_len: usize, count: usize, tracker: &ResourceTracker) -> Result<(), ResourceError> {
check_estimated_size(item_len.saturating_mul(count), tracker)
}
pub fn check_pow_size(base_bits: u64, exponent: u64, tracker: &ResourceTracker) -> Result<(), ResourceError> {
if base_bits <= 1 {
return Ok(());
}
let result_bytes = estimate_bits_to_bytes(base_bits.saturating_mul(exponent));
check_estimated_size(result_bytes.saturating_mul(4), tracker)
}
pub fn check_mult_size(a_bits: u64, b_bits: u64, tracker: &ResourceTracker) -> Result<(), ResourceError> {
check_estimated_size(estimate_bits_to_bytes(a_bits.saturating_add(b_bits)), tracker)
}
pub fn check_lshift_size(value_bits: u64, shift_amount: u64, tracker: &ResourceTracker) -> Result<(), ResourceError> {
if value_bits == 0 {
return Ok(());
}
check_estimated_size(estimate_bits_to_bytes(value_bits.saturating_add(shift_amount)), tracker)
}
pub fn check_div_size(dividend_bits: u64, tracker: &ResourceTracker) -> Result<(), ResourceError> {
check_estimated_size(estimate_bits_to_bytes(dividend_bits), tracker)
}
pub fn check_replace_size(
input_len: usize,
old_len: usize,
new_len: usize,
count: i64,
tracker: &ResourceTracker,
) -> Result<(), ResourceError> {
let estimated = if new_len < old_len {
input_len
} else {
let max_replacements = input_len
.checked_div(old_len)
.unwrap_or_else(|| input_len.saturating_add(1));
let replacements = if count < 0 {
max_replacements
} else {
max_replacements.min(usize::try_from(count).unwrap_or(usize::MAX))
};
let removed = replacements.saturating_mul(old_len);
let added = replacements.saturating_mul(new_len);
input_len.saturating_sub(removed).saturating_add(added)
};
check_estimated_size(estimated, tracker)
}
pub(crate) fn check_estimated_size(estimated_bytes: usize, tracker: &ResourceTracker) -> Result<(), ResourceError> {
if estimated_bytes > LARGE_RESULT_THRESHOLD {
tracker.check_large_result(estimated_bytes)?;
}
Ok(())
}
fn estimate_bits_to_bytes(bits: u64) -> usize {
usize::try_from(bits.saturating_add(7) / 8).unwrap_or(usize::MAX)
}
impl From<ResourceError> for RunError {
fn from(err: ResourceError) -> Self {
let (exc_type, catchable) = match &err {
ResourceError::Memory { .. } => (ExcType::MemoryError, false),
ResourceError::Time { .. } => (ExcType::TimeoutError, false),
ResourceError::Recursion { .. } => (ExcType::RecursionError, true),
};
let exc = SimpleException::new_msg(exc_type, err).into();
if catchable {
Self::Exc(exc)
} else {
Self::UncatchableExc(exc)
}
}
}