use std::collections::HashSet;
use std::time::Instant;
use pd_host_function::pd_host_function;
use crate::{CallOutcome, Value, VmResult};
use super::{host_error, return_int, return_value, with_context};
fn non_negative_index(value: i64, label: &str) -> VmResult<usize> {
usize::try_from(value).map_err(|_| host_error(format!("{label} must be non-negative")))
}
#[pd_host_function(name = "flint::runtime::input")]
pub(super) fn runtime_input_impl(index: i64) -> VmResult<CallOutcome> {
with_context(|context| {
let index = non_negative_index(index, "input index")?;
let handle = *context
.inputs
.get(index)
.ok_or_else(|| host_error(format!("unknown input index {index}")))?;
return_int(handle)
})
}
#[pd_host_function(name = "flint::runtime::arg")]
pub(super) fn runtime_arg_impl(index: i64) -> VmResult<CallOutcome> {
with_context(|context| {
let index = non_negative_index(index, "argument index")?;
let value = context
.args
.get(index)
.ok_or_else(|| host_error(format!("unknown runtime argument {index}")))?
.clone();
return_value(Value::String(value.into()))
})
}
#[pd_host_function(name = "flint::runtime::arg_int")]
pub(super) fn runtime_arg_int_impl(index: i64) -> VmResult<CallOutcome> {
with_context(|context| {
let index = non_negative_index(index, "argument index")?;
let value = context
.args
.get(index)
.ok_or_else(|| host_error(format!("unknown runtime argument {index}")))?
.parse::<i64>()
.map_err(|err| host_error(format!("runtime argument {index} is not an int: {err}")))?;
return_int(value)
})
}
#[pd_host_function(name = "flint::runtime::arg_int_or")]
pub(super) fn runtime_arg_int_or_impl(index: i64, default: i64) -> VmResult<CallOutcome> {
with_context(|context| {
let index = non_negative_index(index, "argument index")?;
let Some(value) = context.args.get(index) else {
return return_int(default);
};
let value = value
.parse::<i64>()
.map_err(|err| host_error(format!("runtime argument {index} is not an int: {err}")))?;
return_int(value)
})
}
#[pd_host_function(name = "flint::runtime::arg_float_or")]
pub(super) fn runtime_arg_float_or_impl(index: i64, default: f64) -> VmResult<CallOutcome> {
with_context(|context| {
let index = non_negative_index(index, "argument index")?;
let Some(value) = context.args.get(index) else {
return return_value(Value::Float(default));
};
let value = value
.parse::<f64>()
.map_err(|err| host_error(format!("runtime argument {index} is not a float: {err}")))?;
return_value(Value::Float(value))
})
}
#[pd_host_function(name = "flint::runtime::arg_or")]
pub(super) fn runtime_arg_or_impl(index: i64, default: &str) -> VmResult<CallOutcome> {
with_context(|context| {
let index = non_negative_index(index, "argument index")?;
let value = context
.args
.get(index)
.cloned()
.unwrap_or_else(|| default.to_owned());
return_value(Value::String(value.into()))
})
}
#[pd_host_function(name = "flint::runtime::set_output")]
pub(super) fn runtime_set_output_impl(tensor: i64) -> VmResult<CallOutcome> {
with_context(|context| {
context.tensor(tensor)?;
context.output = Some(tensor);
return_value(Value::Bool(true))
})
}
#[pd_host_function(name = "flint::runtime::set_text_output")]
pub(super) fn runtime_set_text_output_impl(text: &str) -> VmResult<CallOutcome> {
with_context(|context| {
context.text_output = Some(text.to_owned());
return_value(Value::Bool(true))
})
}
#[pd_host_function(name = "flint::runtime::start_timer")]
pub(super) fn runtime_start_timer_impl() -> VmResult<CallOutcome> {
with_context(|context| {
context.generation_started_at = Some(Instant::now());
context.generated_tokens = None;
context.decode_started_at = None;
context.decode_tokens = None;
context.generated_token_tensors.clear();
return_value(Value::Bool(true))
})
}
#[pd_host_function(name = "flint::runtime::start_decode_timer")]
pub(super) fn runtime_start_decode_timer_impl() -> VmResult<CallOutcome> {
with_context(|context| {
context.decode_started_at = Some(Instant::now());
context.decode_tokens = None;
return_value(Value::Bool(true))
})
}
#[pd_host_function(name = "flint::runtime::set_token_count")]
pub(super) fn runtime_set_token_count_impl(generated_tokens: i64) -> VmResult<CallOutcome> {
with_context(|context| {
if generated_tokens < 0 {
return Err(host_error("generated token count must be non-negative"));
}
context.generated_tokens = Some(generated_tokens);
return_value(Value::Bool(true))
})
}
#[pd_host_function(name = "flint::runtime::set_decode_token_count")]
pub(super) fn runtime_set_decode_token_count_impl(decode_tokens: i64) -> VmResult<CallOutcome> {
with_context(|context| {
if decode_tokens < 0 {
return Err(host_error("decode token count must be non-negative"));
}
context.decode_tokens = Some(decode_tokens);
return_value(Value::Bool(true))
})
}
#[pd_host_function(name = "flint::runtime::compact2")]
pub(super) fn runtime_compact2_impl(first: i64, second: i64) -> VmResult<CallOutcome> {
with_context(|context| {
let mut keep = HashSet::new();
keep.insert(first);
keep.insert(second);
keep.extend(context.inputs.iter().copied());
keep.extend(context.weight_handles.values().copied());
if let Some(output) = context.output {
keep.insert(output);
}
context.tensors.retain(|handle, _| keep.contains(handle));
context.pairs.clear();
context
.weight_handles
.retain(|_, handle| context.tensors.contains_key(handle));
let count = i64::try_from(context.tensors.len())
.map_err(|_| host_error("tensor count exceeds RustScript integer range"))?;
return_int(count)
})
}