1#![cfg_attr(feature = "alloc_trait", feature(allocator_api))]
19#![allow(renamed_and_removed_lints)]
21#![deny(missing_docs, broken_intra_doc_links)]
22#![no_std]
23
24#[cfg(feature = "alloc_trait")]
25use core::alloc::{Alloc, AllocErr, CannotReallocInPlace, Excess};
26use core::alloc::{GlobalAlloc, Layout};
27#[cfg(feature = "alloc_trait")]
28use core::ptr::NonNull;
29
30use libc::{c_int, c_void};
31
32#[cfg(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc"))]
46const ALIGNOF_MAX_ALIGN_T: usize = 8;
47#[cfg(any(
48 target_arch = "x86",
49 target_arch = "x86_64",
50 target_arch = "aarch64",
51 target_arch = "powerpc64",
52 target_arch = "loongarch64",
53 target_arch = "mips64",
54 target_arch = "riscv32",
55 target_arch = "riscv64",
56 target_arch = "s390x",
57 target_arch = "sparc64"
58))]
59const ALIGNOF_MAX_ALIGN_T: usize = 16;
60
61fn layout_to_flags(align: usize, size: usize) -> c_int {
68 if align <= ALIGNOF_MAX_ALIGN_T && align <= size {
69 0
70 } else {
71 ffi::MALLOCX_ALIGN(align)
72 }
73}
74
75macro_rules! assume {
77 ($e:expr) => {
78 debug_assert!($e);
79 if !($e) {
80 core::hint::unreachable_unchecked();
81 }
82 };
83}
84
85#[derive(Copy, Clone, Default, Debug)]
92pub struct Jemalloc;
93
94unsafe impl GlobalAlloc for Jemalloc {
95 #[inline]
96 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
97 assume!(layout.size() != 0);
98 let flags = layout_to_flags(layout.align(), layout.size());
99 let ptr = if flags == 0 {
100 ffi::malloc(layout.size())
101 } else {
102 ffi::mallocx(layout.size(), flags)
103 };
104 ptr as *mut u8
105 }
106
107 #[inline]
108 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
109 assume!(layout.size() != 0);
110 let flags = layout_to_flags(layout.align(), layout.size());
111 let ptr = if flags == 0 {
112 ffi::calloc(1, layout.size())
113 } else {
114 ffi::mallocx(layout.size(), flags | ffi::MALLOCX_ZERO)
115 };
116 ptr as *mut u8
117 }
118
119 #[inline]
120 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
121 assume!(!ptr.is_null());
122 assume!(layout.size() != 0);
123 let flags = layout_to_flags(layout.align(), layout.size());
124 ffi::sdallocx(ptr as *mut c_void, layout.size(), flags)
125 }
126
127 #[inline]
128 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
129 assume!(layout.size() != 0);
130 assume!(new_size != 0);
131 let flags = layout_to_flags(layout.align(), new_size);
132 let ptr = if flags == 0 {
133 ffi::realloc(ptr as *mut c_void, new_size)
134 } else {
135 ffi::rallocx(ptr as *mut c_void, new_size, flags)
136 };
137 ptr as *mut u8
138 }
139}
140
141#[cfg(feature = "alloc_trait")]
142unsafe impl Alloc for Jemalloc {
143 #[inline]
144 unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
145 NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr)
146 }
147
148 #[inline]
149 unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
150 NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr)
151 }
152
153 #[inline]
154 unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
155 GlobalAlloc::dealloc(self, ptr.as_ptr(), layout)
156 }
157
158 #[inline]
159 unsafe fn realloc(
160 &mut self,
161 ptr: NonNull<u8>,
162 layout: Layout,
163 new_size: usize,
164 ) -> Result<NonNull<u8>, AllocErr> {
165 NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr)
166 }
167
168 #[inline]
169 unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
170 let flags = layout_to_flags(layout.align(), layout.size());
171 let ptr = ffi::mallocx(layout.size(), flags);
172 if let Some(nonnull) = NonNull::new(ptr as *mut u8) {
173 let excess = ffi::nallocx(layout.size(), flags);
174 Ok(Excess(nonnull, excess))
175 } else {
176 Err(AllocErr)
177 }
178 }
179
180 #[inline]
181 unsafe fn realloc_excess(
182 &mut self,
183 ptr: NonNull<u8>,
184 layout: Layout,
185 new_size: usize,
186 ) -> Result<Excess, AllocErr> {
187 let flags = layout_to_flags(layout.align(), new_size);
188 let ptr = ffi::rallocx(ptr.cast().as_ptr(), new_size, flags);
189 if let Some(nonnull) = NonNull::new(ptr as *mut u8) {
190 let excess = ffi::nallocx(new_size, flags);
191 Ok(Excess(nonnull, excess))
192 } else {
193 Err(AllocErr)
194 }
195 }
196
197 #[inline]
198 fn usable_size(&self, layout: &Layout) -> (usize, usize) {
199 let flags = layout_to_flags(layout.align(), layout.size());
200 unsafe {
201 let max = ffi::nallocx(layout.size(), flags);
202 (layout.size(), max)
203 }
204 }
205
206 #[inline]
207 unsafe fn grow_in_place(
208 &mut self,
209 ptr: NonNull<u8>,
210 layout: Layout,
211 new_size: usize,
212 ) -> Result<(), CannotReallocInPlace> {
213 let flags = layout_to_flags(layout.align(), new_size);
214 let usable_size = ffi::xallocx(ptr.cast().as_ptr(), new_size, 0, flags);
215 if usable_size >= new_size {
216 Ok(())
217 } else {
218 Err(CannotReallocInPlace)
223 }
224 }
225
226 #[inline]
227 unsafe fn shrink_in_place(
228 &mut self,
229 ptr: NonNull<u8>,
230 layout: Layout,
231 new_size: usize,
232 ) -> Result<(), CannotReallocInPlace> {
233 if new_size == layout.size() {
234 return Ok(());
235 }
236 let flags = layout_to_flags(layout.align(), new_size);
237 let usable_size = ffi::xallocx(ptr.cast().as_ptr(), new_size, 0, flags);
238
239 if usable_size < layout.size() {
240 Ok(())
245 } else if usable_size == ffi::nallocx(new_size, flags) {
246 debug_assert_eq!(
255 ffi::nallocx(new_size, flags),
256 ffi::nallocx(layout.size(), flags)
257 );
258 Ok(())
259 } else {
260 Err(CannotReallocInPlace)
264 }
265 }
266}
267
268pub unsafe fn usable_size<T>(ptr: *const T) -> usize {
281 ffi::malloc_usable_size(ptr as *const c_void)
282}
283
284mod ffi {
286 pub use linera_jemalloc_sys::*;
287}