use crate::compiler::instructions::Instruction;
use crate::error::{Error, ErrorKind};
use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::Arc;
pub struct FuelTracker {
initial: u64,
remaining: AtomicIsize,
}
impl FuelTracker {
pub fn new(fuel: u64) -> Arc<FuelTracker> {
Arc::new(FuelTracker {
initial: fuel,
remaining: AtomicIsize::new(fuel as isize),
})
}
pub fn track(&self, instr: &Instruction) -> Result<(), Error> {
let fuel_to_consume = fuel_for_instruction(instr);
if fuel_to_consume != 0 {
let old_fuel = self.remaining.fetch_sub(fuel_to_consume, Ordering::Relaxed);
if old_fuel - fuel_to_consume <= 0 {
return Err(Error::from(ErrorKind::OutOfFuel));
}
}
Ok(())
}
pub fn remaining(&self) -> u64 {
self.remaining.load(Ordering::Relaxed) as _
}
pub fn consumed(&self) -> u64 {
self.initial.saturating_sub(self.remaining())
}
}
fn fuel_for_instruction(instruction: &Instruction) -> isize {
match instruction {
Instruction::BeginCapture(_)
| Instruction::PushLoop(_)
| Instruction::PushDidNotIterate
| Instruction::PushWith
| Instruction::PopFrame
| Instruction::PopLoopFrame
| Instruction::DupTop
| Instruction::DiscardTop
| Instruction::PushAutoEscape
| Instruction::PopAutoEscape => 0,
#[cfg(feature = "multi_template")]
Instruction::ExportLocals => 0,
#[cfg(feature = "macros")]
Instruction::LoadBlocks | Instruction::BuildMacro(..) | Instruction::Return => 0,
_ => 1,
}
}