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}