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(
46 target_arch = "arm",
47 target_arch = "mips",
48 target_arch = "mipsel",
49 target_arch = "powerpc"
50))]
51const ALIGNOF_MAX_ALIGN_T: usize = 8;
52#[cfg(any(
53 target_arch = "x86",
54 target_arch = "x86_64",
55 target_arch = "aarch64",
56 target_arch = "powerpc64",
57 target_arch = "powerpc64le",
58 target_arch = "loongarch64",
59 target_arch = "mips64",
60 target_arch = "riscv64",
61 target_arch = "s390x",
62 target_arch = "sparc64"
63))]
64const ALIGNOF_MAX_ALIGN_T: usize = 16;
65
66fn layout_to_flags(align: usize, size: usize) -> c_int {
73 if align <= ALIGNOF_MAX_ALIGN_T && align <= size {
74 0
75 } else {
76 ffi::MALLOCX_ALIGN(align)
77 }
78}
79
80macro_rules! assume {
82 ($e:expr) => {
83 debug_assert!($e);
84 if !($e) {
85 core::hint::unreachable_unchecked();
86 }
87 };
88}
89
90#[derive(Copy, Clone, Default, Debug)]
97pub struct Jemalloc;
98
99unsafe impl GlobalAlloc for Jemalloc {
100 #[inline]
101 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
102 assume!(layout.size() != 0);
103 let flags = layout_to_flags(layout.align(), layout.size());
104 let ptr = if flags == 0 {
105 ffi::malloc(layout.size())
106 } else {
107 ffi::mallocx(layout.size(), flags)
108 };
109 ptr as *mut u8
110 }
111
112 #[inline]
113 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
114 assume!(layout.size() != 0);
115 let flags = layout_to_flags(layout.align(), layout.size());
116 let ptr = if flags == 0 {
117 ffi::calloc(1, layout.size())
118 } else {
119 ffi::mallocx(layout.size(), flags | ffi::MALLOCX_ZERO)
120 };
121 ptr as *mut u8
122 }
123
124 #[inline]
125 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
126 assume!(!ptr.is_null());
127 assume!(layout.size() != 0);
128 let flags = layout_to_flags(layout.align(), layout.size());
129 ffi::sdallocx(ptr as *mut c_void, layout.size(), flags)
130 }
131
132 #[inline]
133 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
134 assume!(layout.size() != 0);
135 assume!(new_size != 0);
136 let flags = layout_to_flags(layout.align(), new_size);
137 let ptr = if flags == 0 {
138 ffi::realloc(ptr as *mut c_void, new_size)
139 } else {
140 ffi::rallocx(ptr as *mut c_void, new_size, flags)
141 };
142 ptr as *mut u8
143 }
144}
145
146#[cfg(feature = "alloc_trait")]
147unsafe impl Alloc for Jemalloc {
148 #[inline]
149 unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
150 NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr)
151 }
152
153 #[inline]
154 unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
155 NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr)
156 }
157
158 #[inline]
159 unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
160 GlobalAlloc::dealloc(self, ptr.as_ptr(), layout)
161 }
162
163 #[inline]
164 unsafe fn realloc(
165 &mut self,
166 ptr: NonNull<u8>,
167 layout: Layout,
168 new_size: usize,
169 ) -> Result<NonNull<u8>, AllocErr> {
170 NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr)
171 }
172
173 #[inline]
174 unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
175 let flags = layout_to_flags(layout.align(), layout.size());
176 let ptr = ffi::mallocx(layout.size(), flags);
177 if let Some(nonnull) = NonNull::new(ptr as *mut u8) {
178 let excess = ffi::nallocx(layout.size(), flags);
179 Ok(Excess(nonnull, excess))
180 } else {
181 Err(AllocErr)
182 }
183 }
184
185 #[inline]
186 unsafe fn realloc_excess(
187 &mut self,
188 ptr: NonNull<u8>,
189 layout: Layout,
190 new_size: usize,
191 ) -> Result<Excess, AllocErr> {
192 let flags = layout_to_flags(layout.align(), new_size);
193 let ptr = ffi::rallocx(ptr.cast().as_ptr(), new_size, flags);
194 if let Some(nonnull) = NonNull::new(ptr as *mut u8) {
195 let excess = ffi::nallocx(new_size, flags);
196 Ok(Excess(nonnull, excess))
197 } else {
198 Err(AllocErr)
199 }
200 }
201
202 #[inline]
203 fn usable_size(&self, layout: &Layout) -> (usize, usize) {
204 let flags = layout_to_flags(layout.align(), layout.size());
205 unsafe {
206 let max = ffi::nallocx(layout.size(), flags);
207 (layout.size(), max)
208 }
209 }
210
211 #[inline]
212 unsafe fn grow_in_place(
213 &mut self,
214 ptr: NonNull<u8>,
215 layout: Layout,
216 new_size: usize,
217 ) -> Result<(), CannotReallocInPlace> {
218 let flags = layout_to_flags(layout.align(), new_size);
219 let usable_size = ffi::xallocx(ptr.cast().as_ptr(), new_size, 0, flags);
220 if usable_size >= new_size {
221 Ok(())
222 } else {
223 Err(CannotReallocInPlace)
228 }
229 }
230
231 #[inline]
232 unsafe fn shrink_in_place(
233 &mut self,
234 ptr: NonNull<u8>,
235 layout: Layout,
236 new_size: usize,
237 ) -> Result<(), CannotReallocInPlace> {
238 if new_size == layout.size() {
239 return Ok(());
240 }
241 let flags = layout_to_flags(layout.align(), new_size);
242 let usable_size = ffi::xallocx(ptr.cast().as_ptr(), new_size, 0, flags);
243
244 if usable_size < layout.size() {
245 Ok(())
250 } else if usable_size == ffi::nallocx(new_size, flags) {
251 debug_assert_eq!(
260 ffi::nallocx(new_size, flags),
261 ffi::nallocx(layout.size(), flags)
262 );
263 Ok(())
264 } else {
265 Err(CannotReallocInPlace)
269 }
270 }
271}
272
273pub unsafe fn usable_size<T>(ptr: *const T) -> usize {
286 ffi::malloc_usable_size(ptr as *const c_void)
287}
288
289mod ffi {
291 pub use tikv_jemalloc_sys::*;
292}