#![deny(unsafe_code)]
#[allow(unsafe_code)]
mod inner {
use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicU64, Ordering};
pub static BITS_ERASED: AtomicU64 = AtomicU64::new(0);
pub struct LandauerAllocator;
#[allow(unsafe_code)]
unsafe impl GlobalAlloc for LandauerAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
unsafe { System.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let bits = layout.size() as u64 * 8;
BITS_ERASED.fetch_add(bits, Ordering::Relaxed);
unsafe { System.dealloc(ptr, layout) }
}
}
}
pub use inner::LandauerAllocator;
use inner::BITS_ERASED;
use std::sync::atomic::Ordering;
#[must_use]
pub fn bits_erased() -> u64 {
BITS_ERASED.load(Ordering::Relaxed)
}
#[must_use]
pub fn landauer_cost_joules(bits: u64, temperature_k: f64) -> f64 {
const K_B: f64 = 1.380_649e-23;
bits as f64 * K_B * temperature_k * std::f64::consts::LN_2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bits_erased_monotonically_increases() {
let before = bits_erased();
let v: Vec<u8> = (0..1024).map(|i| i as u8).collect();
drop(v);
let after = bits_erased();
assert!(
after >= before,
"bits_erased must be monotonically non-decreasing: before={before}, after={after}"
);
}
#[test]
fn bits_erased_increases_on_heap_allocation() {
let before = bits_erased();
let _v: Vec<u8> = vec![0u8; 4096];
drop(_v);
let after = bits_erased();
assert!(after >= before, "after={after} should be ≥ before={before}");
}
#[test]
fn landauer_cost_at_300k_matches_constant() {
let cost = landauer_cost_joules(1, 300.0);
let expected = 2.870_979e-21_f64;
let ratio = cost / expected;
assert!(
(ratio - 1.0).abs() < 1e-4,
"Λ(1 bit, 300 K) = {cost:.6e}, expected ≈ {expected:.6e}"
);
}
#[test]
fn landauer_cost_scales_linearly_with_bits() {
let one = landauer_cost_joules(1, 300.0);
let thousand = landauer_cost_joules(1_000, 300.0);
let ratio = thousand / one;
assert!(
(ratio - 1_000.0).abs() < 1.0,
"Λ must scale linearly: ratio={ratio}"
);
}
#[test]
fn landauer_cost_scales_linearly_with_temperature() {
let at_300 = landauer_cost_joules(100, 300.0);
let at_600 = landauer_cost_joules(100, 600.0);
let ratio = at_600 / at_300;
assert!(
(ratio - 2.0).abs() < 1e-10,
"Λ must be proportional to T: ratio={ratio}"
);
}
#[test]
fn landauer_cost_zero_bits_is_zero() {
let cost = landauer_cost_joules(0, 300.0);
assert_eq!(cost, 0.0, "0 bits erased → 0 joules");
}
}