use std::{array, hint};
use crate::mem::{self, credit::fair};
#[derive(Clone, Copy, Debug)]
#[must_use]
pub struct Balance<const N: usize, W: mem::raw::Word> {
held: [W; N],
shared: [W; N],
}
impl<const N: usize, W: mem::raw::Word> Balance<N, W> {
pub const fn new(held: [W; N], shared: [W; N]) -> Self {
const { assert!(N != 0) };
Self { held, shared }
}
pub fn into_parts(self) -> ([W; N], [W; N]) {
(self.held, self.shared)
}
pub fn try_acquire(self, reserve: [W; N], amount: [W; N]) -> Result<Self, fair::Cause<W, N>> {
let mut shared = self.shared;
let mut next = self.held;
for index in 0..N {
let current = self.held[index];
let request = amount[index];
let Some(total) = current.checked_add(request) else {
hint::cold_path();
return Err(fair::Cause::Overflow {
axis: fair::Axis::<N>::ALL[index],
held: current,
requested: request,
});
};
let required = Self::required(current, reserve[index], request);
if required > shared[index] {
return Err(fair::Cause::Exhausted {
axis: fair::Axis::<N>::ALL[index],
required,
available: shared[index],
});
}
next[index] = total;
shared[index] = shared[index] - required;
}
Ok(Self::new(next, shared))
}
pub unsafe fn acquire_raw(self, reserve: [W; N], amount: [W; N]) -> Self {
let mut next = self.held;
let mut shared = self.shared;
for axis in 0..N {
let total = self.held[axis] + amount[axis];
let required = Self::required(self.held[axis], reserve[axis], amount[axis]);
next[axis] = total;
shared[axis] = shared[axis] - required;
}
Self::new(next, shared)
}
pub fn demand(self, reserve: [W; N], amount: [W; N]) -> [W; N] {
array::from_fn(|axis| Self::required(self.held[axis], reserve[axis], amount[axis]))
}
pub unsafe fn release_raw(self, reserve: [W; N], amount: [W; N]) -> Self {
let mut next = self.held;
let mut shared = self.shared;
for axis in 0..N {
next[axis] = self.held[axis] - amount[axis];
let returned = self.held[axis].sub_or_zero(reserve[axis]).min(amount[axis]);
shared[axis] = shared[axis] + returned;
}
Self::new(next, shared)
}
fn required(held: W, reserve: W, amount: W) -> W {
amount.sub_or_zero(reserve.sub_or_zero(held))
}
}