use core::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct AllocatorStats {
total_pages: usize,
free_pages: usize,
allocations: u64,
successful_allocations: u64,
frees: u64,
splits: u64,
coalesces: u64,
peak_used_pages: usize,
}
impl AllocatorStats {
#[inline]
#[must_use]
pub const fn new(total_pages: usize, free_pages: usize) -> Self {
Self {
total_pages,
free_pages,
allocations: 0,
successful_allocations: 0,
frees: 0,
splits: 0,
coalesces: 0,
peak_used_pages: total_pages.saturating_sub(free_pages),
}
}
#[inline]
#[must_use]
pub const fn total_pages(&self) -> usize {
self.total_pages
}
#[inline]
#[must_use]
pub const fn free_pages(&self) -> usize {
self.free_pages
}
#[inline]
#[must_use]
pub const fn used_pages(&self) -> usize {
self.total_pages.saturating_sub(self.free_pages)
}
#[inline]
#[must_use]
pub const fn total_bytes(&self) -> usize {
self.total_pages * crate::PAGE_SIZE
}
#[inline]
#[must_use]
pub const fn free_bytes(&self) -> usize {
self.free_pages * crate::PAGE_SIZE
}
#[inline]
#[must_use]
pub const fn used_bytes(&self) -> usize {
self.used_pages() * crate::PAGE_SIZE
}
#[inline]
#[must_use]
pub const fn utilization_percent(&self) -> u8 {
if self.total_pages == 0 {
0
} else {
((self.used_pages() * 100) / self.total_pages) as u8
}
}
#[inline]
#[must_use]
pub const fn allocations(&self) -> u64 {
self.allocations
}
#[inline]
#[must_use]
pub const fn successful_allocations(&self) -> u64 {
self.successful_allocations
}
#[inline]
#[must_use]
pub const fn failed_allocations(&self) -> u64 {
self.allocations.saturating_sub(self.successful_allocations)
}
#[inline]
#[must_use]
pub const fn frees(&self) -> u64 {
self.frees
}
#[inline]
#[must_use]
pub const fn splits(&self) -> u64 {
self.splits
}
#[inline]
#[must_use]
pub const fn coalesces(&self) -> u64 {
self.coalesces
}
#[inline]
#[must_use]
pub const fn peak_used_pages(&self) -> usize {
self.peak_used_pages
}
#[inline]
#[must_use]
pub const fn peak_used_bytes(&self) -> usize {
self.peak_used_pages * crate::PAGE_SIZE
}
#[inline]
pub fn record_allocation(&mut self, pages: usize, success: bool) {
self.allocations += 1;
if success {
self.successful_allocations += 1;
self.free_pages = self.free_pages.saturating_sub(pages);
let used = self.used_pages();
if used > self.peak_used_pages {
self.peak_used_pages = used;
}
}
}
#[inline]
pub fn record_free(&mut self, pages: usize) {
self.frees += 1;
self.free_pages = (self.free_pages + pages).min(self.total_pages);
}
#[inline]
pub fn record_split(&mut self) {
self.splits += 1;
}
#[inline]
pub fn record_coalesce(&mut self) {
self.coalesces += 1;
}
#[inline]
pub fn reset_counters(&mut self) {
self.allocations = 0;
self.successful_allocations = 0;
self.frees = 0;
self.splits = 0;
self.coalesces = 0;
self.peak_used_pages = self.used_pages();
}
#[inline]
#[must_use]
pub const fn success_rate_percent(&self) -> u8 {
if self.allocations == 0 {
100
} else {
((self.successful_allocations * 100) / self.allocations) as u8
}
}
#[inline]
#[must_use]
pub fn splits_per_allocation(&self) -> f64 {
if self.successful_allocations == 0 {
0.0
} else {
self.splits as f64 / self.successful_allocations as f64
}
}
#[inline]
#[must_use]
pub fn coalesces_per_free(&self) -> f64 {
if self.frees == 0 {
0.0
} else {
self.coalesces as f64 / self.frees as f64
}
}
}
impl fmt::Debug for AllocatorStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AllocatorStats")
.field("total_pages", &self.total_pages)
.field("free_pages", &self.free_pages)
.field("used_pages", &self.used_pages())
.field("utilization_percent", &self.utilization_percent())
.field("allocations", &self.allocations)
.field("successful", &self.successful_allocations)
.field("frees", &self.frees)
.field("splits", &self.splits)
.field("coalesces", &self.coalesces)
.finish()
}
}
impl fmt::Display for AllocatorStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Memory: {}/{} pages ({}%), Allocs: {} ({} failed), Frees: {}",
self.used_pages(),
self.total_pages,
self.utilization_percent(),
self.successful_allocations,
self.failed_allocations(),
self.frees
)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct OrderStats {
pub free_blocks: usize,
pub allocations: u64,
pub frees: u64,
}
impl OrderStats {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
free_blocks: 0,
allocations: 0,
frees: 0,
}
}
}
impl fmt::Debug for OrderStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OrderStats")
.field("free_blocks", &self.free_blocks)
.field("allocations", &self.allocations)
.field("frees", &self.frees)
.finish()
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::format;
use super::*;
#[test]
fn test_new_stats() {
let stats = AllocatorStats::new(1024, 1024);
assert_eq!(stats.total_pages(), 1024);
assert_eq!(stats.free_pages(), 1024);
assert_eq!(stats.used_pages(), 0);
assert_eq!(stats.utilization_percent(), 0);
}
#[test]
fn test_record_allocation() {
let mut stats = AllocatorStats::new(1024, 1024);
stats.record_allocation(10, true);
assert_eq!(stats.allocations(), 1);
assert_eq!(stats.successful_allocations(), 1);
assert_eq!(stats.free_pages(), 1014);
assert_eq!(stats.used_pages(), 10);
stats.record_allocation(5, false);
assert_eq!(stats.allocations(), 2);
assert_eq!(stats.successful_allocations(), 1);
assert_eq!(stats.failed_allocations(), 1);
assert_eq!(stats.free_pages(), 1014); }
#[test]
fn test_record_free() {
let mut stats = AllocatorStats::new(1024, 1000);
stats.record_free(10);
assert_eq!(stats.frees(), 1);
assert_eq!(stats.free_pages(), 1010);
stats.record_free(100);
assert_eq!(stats.free_pages(), 1024);
}
#[test]
fn test_peak_tracking() {
let mut stats = AllocatorStats::new(100, 100);
stats.record_allocation(30, true);
assert_eq!(stats.peak_used_pages(), 30);
stats.record_allocation(20, true);
assert_eq!(stats.peak_used_pages(), 50);
stats.record_free(40);
assert_eq!(stats.peak_used_pages(), 50);
stats.record_allocation(60, true);
assert_eq!(stats.peak_used_pages(), 70);
}
#[test]
fn test_splits_coalesces() {
let mut stats = AllocatorStats::new(1024, 1024);
stats.record_split();
stats.record_split();
stats.record_coalesce();
assert_eq!(stats.splits(), 2);
assert_eq!(stats.coalesces(), 1);
}
#[test]
fn test_utilization() {
let stats = AllocatorStats::new(100, 50);
assert_eq!(stats.utilization_percent(), 50);
let stats = AllocatorStats::new(100, 100);
assert_eq!(stats.utilization_percent(), 0);
let stats = AllocatorStats::new(100, 0);
assert_eq!(stats.utilization_percent(), 100);
let stats = AllocatorStats::new(0, 0);
assert_eq!(stats.utilization_percent(), 0);
}
#[test]
fn test_success_rate() {
let mut stats = AllocatorStats::new(1024, 1024);
assert_eq!(stats.success_rate_percent(), 100);
stats.record_allocation(10, true);
assert_eq!(stats.success_rate_percent(), 100);
stats.record_allocation(10, false);
assert_eq!(stats.success_rate_percent(), 50);
stats.record_allocation(10, true);
assert_eq!(stats.success_rate_percent(), 66); }
#[test]
fn test_bytes_calculations() {
let stats = AllocatorStats::new(256, 128);
assert_eq!(stats.total_bytes(), 256 * 4096);
assert_eq!(stats.free_bytes(), 128 * 4096);
assert_eq!(stats.used_bytes(), 128 * 4096);
}
#[test]
fn test_reset_counters() {
let mut stats = AllocatorStats::new(100, 50);
stats.allocations = 100;
stats.successful_allocations = 90;
stats.frees = 50;
stats.splits = 20;
stats.coalesces = 10;
stats.peak_used_pages = 80;
stats.reset_counters();
assert_eq!(stats.allocations(), 0);
assert_eq!(stats.successful_allocations(), 0);
assert_eq!(stats.frees(), 0);
assert_eq!(stats.splits(), 0);
assert_eq!(stats.coalesces(), 0);
assert_eq!(stats.peak_used_pages(), 50); assert_eq!(stats.free_pages(), 50); }
#[test]
fn test_display() {
let mut stats = AllocatorStats::new(1024, 512);
stats.allocations = 10;
stats.successful_allocations = 8;
stats.frees = 3;
let s = format!("{stats}");
assert!(s.contains("512/1024"));
assert!(s.contains("50%"));
assert!(s.contains("8"));
assert!(s.contains("2 failed"));
}
#[test]
fn test_order_stats() {
let mut os = OrderStats::new();
assert_eq!(os.free_blocks, 0);
assert_eq!(os.allocations, 0);
assert_eq!(os.frees, 0);
os.free_blocks = 5;
os.allocations = 10;
os.frees = 3;
assert_eq!(os.free_blocks, 5);
}
}