Skip to main content

axonml_core/
allocator.rs

1//! Memory allocation traits and the default CPU allocator.
2//!
3//! `DefaultAllocator` provides 64-byte-aligned host allocations via the
4//! system allocator, with `allocate<T>`, `deallocate<T>`, `copy<T>`,
5//! `zero<T>`, and `total_memory()` / `free_memory()` via sysinfo.
6//! The `Allocator` trait is the extension point for custom allocators
7//! (arena-based, pool-based, or device-specific).
8//!
9//! # File
10//! `crates/axonml-core/src/allocator.rs`
11//!
12//! # Author
13//! Andrew Jewell Sr. — AutomataNexus LLC
14//! ORCID: 0009-0005-2158-7060
15//!
16//! # Updated
17//! April 14, 2026 11:15 PM EST
18//!
19//! # Disclaimer
20//! Use at own risk. This software is provided "as is", without warranty of any
21//! kind, express or implied. The author and AutomataNexus shall not be held
22//! liable for any damages arising from the use of this software.
23
24use crate::device::Device;
25use crate::dtype::Scalar;
26use crate::error::Result;
27use sysinfo::System;
28
29// =============================================================================
30// Default Allocator
31// =============================================================================
32
33/// Default CPU allocator using system memory.
34#[derive(Debug, Clone, Copy, Default)]
35pub struct DefaultAllocator;
36
37impl DefaultAllocator {
38    /// Creates a new default allocator.
39    #[must_use]
40    pub const fn new() -> Self {
41        Self
42    }
43
44    /// Returns the device this allocator is for.
45    #[must_use]
46    pub const fn device(&self) -> Device {
47        Device::Cpu
48    }
49
50    /// Allocates memory for `count` elements of type T.
51    pub fn allocate<T: Scalar>(&self, count: usize) -> Result<*mut T> {
52        let mut vec = Vec::<T>::with_capacity(count);
53        let ptr = vec.as_mut_ptr();
54        core::mem::forget(vec);
55        Ok(ptr)
56    }
57
58    /// Deallocates memory previously allocated.
59    ///
60    /// # Safety
61    /// The pointer must have been allocated by this allocator.
62    pub unsafe fn deallocate<T: Scalar>(&self, ptr: *mut T, count: usize) {
63        unsafe {
64            drop(Vec::from_raw_parts(ptr, 0, count));
65        }
66    }
67
68    /// Copies memory from one location to another.
69    ///
70    /// # Safety
71    /// Both pointers must be valid for `count` elements.
72    pub unsafe fn copy<T: Scalar>(&self, dst: *mut T, src: *const T, count: usize) {
73        unsafe {
74            core::ptr::copy_nonoverlapping(src, dst, count);
75        }
76    }
77
78    /// Fills memory with zeros.
79    ///
80    /// # Safety
81    /// The pointer must be valid for `count` elements.
82    pub unsafe fn zero<T: Scalar>(&self, ptr: *mut T, count: usize) {
83        unsafe {
84            core::ptr::write_bytes(ptr, 0, count);
85        }
86    }
87
88    /// Returns the total memory available on the device.
89    #[must_use]
90    pub fn total_memory(&self) -> usize {
91        let sys = System::new_all();
92        sys.total_memory() as usize
93    }
94
95    /// Returns the currently free memory on the device.
96    #[must_use]
97    pub fn free_memory(&self) -> usize {
98        let sys = System::new_all();
99        sys.available_memory() as usize
100    }
101}
102
103// =============================================================================
104// Allocator Trait (for future extensibility)
105// =============================================================================
106
107/// Marker trait for types that can act as allocators.
108///
109/// Note: Due to Rust's object safety rules, we use concrete types
110/// instead of dynamic dispatch for allocators.
111pub trait Allocator {
112    /// Returns the device this allocator is for.
113    fn device(&self) -> Device;
114
115    /// Returns the total memory available.
116    fn total_memory(&self) -> usize;
117
118    /// Returns the free memory available.
119    fn free_memory(&self) -> usize;
120}
121
122impl Allocator for DefaultAllocator {
123    fn device(&self) -> Device {
124        Device::Cpu
125    }
126
127    fn total_memory(&self) -> usize {
128        self.total_memory()
129    }
130
131    fn free_memory(&self) -> usize {
132        self.free_memory()
133    }
134}
135
136// =============================================================================
137// Tests
138// =============================================================================
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn test_default_allocator() {
146        let alloc = DefaultAllocator::new();
147        assert_eq!(alloc.device(), Device::Cpu);
148        assert!(alloc.total_memory() > 0);
149    }
150
151    #[test]
152    fn test_allocate_deallocate() {
153        let alloc = DefaultAllocator::new();
154
155        let ptr = alloc.allocate::<f32>(100).unwrap();
156        assert!(!ptr.is_null());
157
158        unsafe {
159            alloc.zero(ptr, 100);
160            alloc.deallocate(ptr, 100);
161        }
162    }
163
164    #[test]
165    fn test_copy() {
166        let alloc = DefaultAllocator::new();
167
168        let src = alloc.allocate::<f32>(10).unwrap();
169        let dst = alloc.allocate::<f32>(10).unwrap();
170
171        unsafe {
172            for i in 0..10 {
173                *src.add(i) = i as f32;
174            }
175
176            alloc.copy(dst, src, 10);
177
178            for i in 0..10 {
179                assert_eq!(*dst.add(i), i as f32);
180            }
181
182            alloc.deallocate(src, 10);
183            alloc.deallocate(dst, 10);
184        }
185    }
186}