Skip to main content

kalloc/
lib.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#![no_std]
8
9#[cfg(not(is_kernel))]
10extern crate alloc;
11
12use core::alloc::Layout;
13use core::ptr::NonNull;
14
15mod allocator;
16mod boxed;
17
18pub use allocator::{AllocError, Allocator, DefaultAllocator, NoOpAllocator};
19pub use boxed::Box;
20
21/// Fallible allocation matching standard alloc interface.
22///
23/// # Safety
24///
25/// - `layout` must have non-zero size.
26#[cfg(not(is_kernel))]
27pub unsafe fn alloc(layout: Layout) -> Option<NonNull<u8>> {
28    debug_assert!(layout.size() > 0);
29    let ptr = unsafe { alloc::alloc::alloc(layout) };
30    NonNull::new(ptr)
31}
32
33/// Fallible deallocation matching standard alloc interface.
34///
35/// # Safety
36///
37/// - `ptr` is a block of memory currently allocated via this allocator and,
38/// - `layout` is the same layout that was used to allocate that block of memory.
39#[cfg(not(is_kernel))]
40pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) {
41    debug_assert!(layout.size() > 0);
42    unsafe { alloc::alloc::dealloc(ptr, layout) }
43}
44
45/// Fallible reallocation matching standard alloc interface.
46///
47/// # Safety
48///
49/// - `ptr` is allocated via this allocator,
50/// - `layout` is the same layout that was used to allocate that block of memory,
51/// - `new_size` is greater than zero,
52/// - `new_size`, when rounded up to the nearest multiple of `layout.align()`, does not overflow isize.
53#[cfg(not(is_kernel))]
54pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> Option<NonNull<u8>> {
55    debug_assert!(layout.size() > 0);
56    debug_assert!(new_size > 0);
57    let ptr = unsafe { alloc::alloc::realloc(ptr, layout, new_size) };
58    NonNull::new(ptr)
59}
60
61/// Fallible zeroed allocation matching standard alloc interface.
62///
63/// # Safety
64///
65/// - `layout` must have non-zero size.
66#[cfg(not(is_kernel))]
67pub unsafe fn alloc_zeroed(layout: Layout) -> Option<NonNull<u8>> {
68    debug_assert!(layout.size() > 0);
69    let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) };
70    NonNull::new(ptr)
71}
72
73/// Fallible allocation using kernel malloc.
74///
75/// # Safety
76///
77/// - `layout` must have non-zero size.
78#[cfg(is_kernel)]
79pub unsafe fn alloc(layout: Layout) -> Option<NonNull<u8>> {
80    debug_assert!(layout.size() > 0);
81    unsafe extern "C" {
82        fn malloc(size: usize) -> *mut core::ffi::c_void;
83        fn memalign(alignment: usize, size: usize) -> *mut core::ffi::c_void;
84    }
85    let ptr = if layout.align() <= 8 {
86        unsafe { malloc(layout.size()) as *mut u8 }
87    } else {
88        unsafe { memalign(layout.align(), layout.size()) as *mut u8 }
89    };
90    NonNull::new(ptr)
91}
92
93/// Fallible deallocation using kernel free.
94///
95/// # Safety
96///
97/// - `ptr` is a block of memory currently allocated via this allocator and,
98/// - `layout` is the same layout that was used to allocate that block of memory.
99#[cfg(is_kernel)]
100pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) {
101    debug_assert!(layout.size() > 0);
102    unsafe extern "C" {
103        fn free(ptr: *mut core::ffi::c_void);
104    }
105    unsafe { free(ptr as *mut core::ffi::c_void) }
106}
107
108/// Fallible reallocation using kernel realloc.
109///
110/// # Safety
111///
112/// - `ptr` is allocated via this allocator,
113/// - `layout` is the same layout that was used to allocate that block of memory,
114/// - `new_size` is greater than zero,
115/// - `new_size`, when rounded up to the nearest multiple of `layout.align()`, does not overflow isize.
116#[cfg(is_kernel)]
117pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> Option<NonNull<u8>> {
118    debug_assert!(layout.size() > 0);
119    debug_assert!(new_size > 0);
120
121    // Zircon kernel's cmpctmalloc doesn't implement a C-linkage `realloc`, so we
122    // emulate it using alloc, copy, and dealloc.
123    let new_layout = Layout::from_size_align(new_size, layout.align()).ok()?;
124    let new_ptr = unsafe { crate::alloc(new_layout) };
125
126    if let Some(new_p) = new_ptr {
127        let copy_size = core::cmp::min(layout.size(), new_size);
128        unsafe {
129            core::ptr::copy_nonoverlapping(ptr, new_p.as_ptr(), copy_size);
130            crate::dealloc(ptr, layout);
131        }
132    }
133    new_ptr
134}
135
136/// Fallible zeroed allocation using kernel calloc.
137///
138/// # Safety
139///
140/// - `layout` must have non-zero size.
141#[cfg(is_kernel)]
142pub unsafe fn alloc_zeroed(layout: Layout) -> Option<NonNull<u8>> {
143    debug_assert!(layout.size() > 0);
144    if layout.align() <= 8 {
145        unsafe extern "C" {
146            fn calloc(nmemb: usize, size: usize) -> *mut core::ffi::c_void;
147        }
148        let ptr = unsafe { calloc(1, layout.size()) as *mut u8 };
149        NonNull::new(ptr)
150    } else {
151        let ptr = unsafe { crate::alloc(layout) };
152        if let Some(p) = ptr {
153            unsafe {
154                core::ptr::write_bytes(p.as_ptr(), 0, layout.size());
155            }
156        }
157        ptr
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn test_kalloc_alloc() {
167        let layout = Layout::from_size_align(10, 1).unwrap();
168        unsafe {
169            let ptr = crate::alloc(layout).unwrap();
170            crate::dealloc(ptr.as_ptr(), layout);
171        }
172    }
173
174    #[test]
175    fn test_kalloc_realloc() {
176        let layout = Layout::from_size_align(10, 1).unwrap();
177        unsafe {
178            let ptr = crate::alloc(layout).unwrap();
179            let new_ptr = crate::realloc(ptr.as_ptr(), layout, 20).unwrap();
180            crate::dealloc(new_ptr.as_ptr(), Layout::from_size_align(20, 1).unwrap());
181        }
182    }
183
184    #[test]
185    fn test_kalloc_alloc_zeroed() {
186        let layout = Layout::from_size_align(10, 1).unwrap();
187        unsafe {
188            let ptr = crate::alloc_zeroed(layout).unwrap();
189            let slice = core::slice::from_raw_parts(ptr.as_ptr(), 10);
190            assert!(slice.iter().all(|&b| b == 0));
191            crate::dealloc(ptr.as_ptr(), layout);
192        }
193    }
194}