use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use truce_params::METER_ID_BASE;
const NUM_SLOTS: usize = 256;
pub struct MeterStore {
slots: [AtomicU32; NUM_SLOTS],
}
impl MeterStore {
#[must_use]
pub fn new() -> Arc<Self> {
Arc::new(Self {
slots: std::array::from_fn(|_| AtomicU32::new(0)),
})
}
#[must_use]
pub fn read(&self, meter_id: u32) -> f32 {
let idx = meter_id.wrapping_sub(METER_ID_BASE) as usize;
self.slots
.get(idx)
.map_or(0.0, |slot| f32::from_bits(slot.load(Ordering::Relaxed)))
}
pub fn write(&self, meter_id: u32, value: f32) {
let idx = meter_id.wrapping_sub(METER_ID_BASE) as usize;
if let Some(slot) = self.slots.get(idx) {
slot.store(value.to_bits(), Ordering::Relaxed);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips_by_meter_id() {
let store = MeterStore::new();
store.write(METER_ID_BASE, 0.5);
store.write(METER_ID_BASE + 255, -1.0);
assert!((store.read(METER_ID_BASE) - 0.5).abs() < f32::EPSILON);
assert!((store.read(METER_ID_BASE + 255) + 1.0).abs() < f32::EPSILON);
}
#[test]
fn out_of_range_ids_are_inert() {
let store = MeterStore::new();
store.write(0, 1.0);
store.write(METER_ID_BASE + 256, 1.0);
assert!(store.read(0).abs() < f32::EPSILON);
assert!(store.read(METER_ID_BASE + 256).abs() < f32::EPSILON);
}
}