use std::marker::PhantomData;
pub struct Z;
pub struct S<N>(PhantomData<N>);
pub type N0 = Z;
pub type N1 = S<N0>;
pub type N2 = S<N1>;
pub type N3 = S<N2>;
pub type N4 = S<N3>;
pub type N5 = S<N4>;
pub type N8 = S<S<S<N5>>>; pub type N16 = S<S<S<S<S<S<S<S<N8>>>>>>>>; pub type N32 = S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<S<N16>>>>>>>>>>>>>>>>;
pub struct Budget<N> {
_remaining: PhantomData<N>,
}
impl<N> Budget<N> {
pub fn new() -> Self {
Budget {
_remaining: PhantomData,
}
}
}
pub trait Sub<Rhs> {
type Output;
}
impl<N> Sub<Z> for N {
type Output = N;
}
impl<N, M> Sub<S<M>> for S<N>
where
N: Sub<M>,
{
type Output = <N as Sub<M>>::Output;
}
pub struct Reg<T: Copy, R> {
value: T,
_registers: PhantomData<R>,
}
impl<T: Copy, R> Reg<T, R> {
pub fn new(value: T) -> Self {
Reg {
value,
_registers: PhantomData,
}
}
pub fn get(&self) -> T {
self.value
}
}
pub trait RegisterSize {
type Size;
}
impl RegisterSize for i32 {
type Size = N1;
}
impl RegisterSize for u32 {
type Size = N1;
}
impl RegisterSize for f32 {
type Size = N1;
}
impl RegisterSize for i64 {
type Size = N2;
}
impl RegisterSize for u64 {
type Size = N2;
}
impl RegisterSize for f64 {
type Size = N2;
}
impl RegisterSize for bool {
type Size = N1;
}
impl<T: RegisterSize, const N: usize> RegisterSize for [T; N]
where
{
type Size = N32; }
pub fn alloc<T, R, B>(_budget: Budget<B>, value: T) -> (Budget<<B as Sub<R>>::Output>, Reg<T, R>)
where
T: Copy + RegisterSize<Size = R>,
B: Sub<R>,
{
(Budget::new(), Reg::new(value))
}
pub fn free<T: Copy, R, B>(_budget: Budget<B>, _reg: Reg<T, R>) -> Budget<S<B>>
where
{
Budget::new()
}
pub mod runtime {
#[derive(Debug, Clone)]
pub struct RegisterTracker {
used: usize,
limit: usize,
peak: usize,
}
impl RegisterTracker {
pub fn new(limit: usize) -> Self {
RegisterTracker {
used: 0,
limit,
peak: 0,
}
}
pub fn alloc(&mut self, count: usize) -> bool {
if self.used + count > self.limit {
return false;
}
self.used += count;
self.peak = self.peak.max(self.used);
true
}
pub fn free(&mut self, count: usize) {
self.used = self.used.saturating_sub(count);
}
pub fn used(&self) -> usize {
self.used
}
pub fn remaining(&self) -> usize {
self.limit - self.used
}
pub fn peak(&self) -> usize {
self.peak
}
}
pub struct TrackedValue<T> {
value: T,
reg_count: usize,
}
impl<T> TrackedValue<T> {
pub fn new(tracker: &mut RegisterTracker, value: T, reg_count: usize) -> Option<Self> {
if tracker.alloc(reg_count) {
Some(TrackedValue { value, reg_count })
} else {
None
}
}
pub fn get(&self) -> &T {
&self.value
}
pub fn free(self, tracker: &mut RegisterTracker) -> T {
tracker.free(self.reg_count);
self.value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_tracking() {
let mut tracker = RegisterTracker::new(64);
assert_eq!(tracker.used(), 0);
assert_eq!(tracker.remaining(), 64);
assert!(tracker.alloc(10));
assert_eq!(tracker.used(), 10);
assert!(tracker.alloc(50));
assert_eq!(tracker.used(), 60);
assert!(!tracker.alloc(10));
assert_eq!(tracker.used(), 60);
tracker.free(20);
assert_eq!(tracker.used(), 40);
assert_eq!(tracker.peak(), 60);
}
#[test]
fn test_tracked_values() {
let mut tracker = RegisterTracker::new(10);
let v1 = TrackedValue::new(&mut tracker, 42i32, 4).unwrap();
assert_eq!(tracker.used(), 4);
let v2 = TrackedValue::new(&mut tracker, 3.14f32, 4).unwrap();
assert_eq!(tracker.used(), 8);
assert!(TrackedValue::new(&mut tracker, 0u64, 4).is_none());
let val1 = v1.free(&mut tracker);
assert_eq!(val1, 42);
assert_eq!(tracker.used(), 4);
let _v3 = TrackedValue::new(&mut tracker, 0u64, 4).unwrap();
assert_eq!(tracker.used(), 8);
let _ = v2; }
}
}
pub const _SUMMARY: () = ();
#[cfg(test)]
mod type_level_tests {
use super::*;
#[test]
fn test_type_level_allocation() {
let budget: Budget<N5> = Budget::new();
let (budget, reg1): (Budget<N4>, Reg<i32, N1>) = alloc(budget, 42i32);
let (budget, reg2): (Budget<N3>, Reg<i32, N1>) = alloc(budget, 10i32);
assert_eq!(reg1.get() + reg2.get(), 52);
let _: Budget<N3> = budget;
}
}