musli_core/alloc/
global.rs1use core::alloc::Layout;
2use core::cmp;
3use core::mem::{align_of, size_of};
4use core::ptr::NonNull;
5
6use rust_alloc::alloc;
7
8use super::{Alloc, AllocError, Allocator, GlobalAllocator};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35#[non_exhaustive]
36pub struct Global;
37
38impl Global {
39 #[inline]
41 pub const fn new() -> Self {
42 Self
43 }
44}
45
46impl Default for Global {
47 #[inline]
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53unsafe impl GlobalAllocator for Global {
54 #[inline]
55 fn __do_not_implement() {}
56
57 #[inline]
58 fn new() -> Self {
59 Self
60 }
61
62 #[inline]
63 fn clone_alloc<T>(alloc: &Self::Alloc<T>) -> Self::Alloc<T> {
64 if size_of::<T>() == 0 {
65 return GlobalAlloc {
68 data: NonNull::dangling(),
69 size: alloc.size,
70 };
71 }
72
73 if alloc.size == 0 {
74 return GlobalAlloc::DANGLING;
75 }
76
77 unsafe {
78 let layout =
80 Layout::from_size_align_unchecked(alloc.size * size_of::<T>(), align_of::<T>());
81 let data = alloc::alloc(layout);
82
83 if data.is_null() {
84 alloc::handle_alloc_error(layout);
85 }
86
87 GlobalAlloc {
88 data: NonNull::new_unchecked(data).cast(),
89 size: alloc.size,
90 }
91 }
92 }
93
94 #[inline]
95 fn slice_from_raw_parts<T>(ptr: NonNull<T>, len: usize) -> Self::Alloc<T> {
96 GlobalAlloc {
97 data: ptr,
98 size: len,
99 }
100 }
101}
102
103unsafe impl Allocator for Global {
104 #[inline]
105 fn __do_not_implement() {}
106
107 const IS_GLOBAL: bool = true;
108
109 type Alloc<T> = GlobalAlloc<T>;
110
111 #[inline]
112 fn alloc<T>(self, value: T) -> Result<Self::Alloc<T>, AllocError> {
113 let mut raw = GlobalAlloc::<T>::alloc()?;
114
115 if size_of::<T>() != 0 {
116 unsafe {
118 raw.as_mut_ptr().write(value);
119 }
120 }
121
122 Ok(raw)
123 }
124
125 #[inline]
126 fn alloc_empty<T>(self) -> Self::Alloc<T> {
127 GlobalAlloc::DANGLING
128 }
129}
130
131pub struct GlobalAlloc<T> {
133 data: NonNull<T>,
135 size: usize,
137}
138
139impl<T> GlobalAlloc<T> {
140 #[must_use = "allocating is fallible and must be checked"]
146 fn alloc() -> Result<Self, AllocError> {
147 if size_of::<T>() == 0 {
148 return Ok(Self {
149 data: NonNull::dangling(),
150 size: 1,
151 });
152 }
153
154 unsafe {
155 let data = alloc::alloc(Layout::new::<T>());
156
157 if data.is_null() {
158 return Err(AllocError);
159 }
160
161 Ok(Self {
162 data: NonNull::new_unchecked(data).cast(),
163 size: 1,
164 })
165 }
166 }
167}
168
169unsafe impl<T> Send for GlobalAlloc<T> where T: Send {}
170unsafe impl<T> Sync for GlobalAlloc<T> where T: Sync {}
171
172impl<T> Alloc<T> for GlobalAlloc<T> {
173 #[inline]
174 fn as_ptr(&self) -> *const T {
175 self.data.as_ptr().cast_const().cast()
176 }
177
178 #[inline]
179 fn as_mut_ptr(&mut self) -> *mut T {
180 self.data.as_ptr().cast()
181 }
182
183 #[inline]
184 fn capacity(&self) -> usize {
185 if size_of::<T>() == 0 {
186 usize::MAX
187 } else {
188 self.size
189 }
190 }
191
192 #[inline]
193 fn resize(&mut self, len: usize, additional: usize) -> Result<(), AllocError> {
194 if size_of::<T>() == 0 {
195 return Ok(());
196 }
197
198 if !self.reserve(len, additional) {
199 return Err(AllocError);
200 }
201
202 Ok(())
203 }
204
205 #[inline]
206 fn try_merge<B>(&mut self, _: usize, other: B, _: usize) -> Result<(), B>
207 where
208 B: Alloc<T>,
209 {
210 if size_of::<T>() == 0 {
211 return Ok(());
212 }
213
214 Err(other)
215 }
216}
217
218impl<T> GlobalAlloc<T> {
219 const MIN_NON_ZERO_CAP: usize = if size_of::<T>() == 1 {
220 8
221 } else if size_of::<T>() <= 1024 {
222 4
223 } else {
224 1
225 };
226
227 const DANGLING: Self = Self {
228 data: NonNull::dangling(),
229 size: 0,
230 };
231
232 #[must_use = "allocating is fallible and must be checked"]
238 fn realloc(&mut self, new_layout: Layout) -> bool {
239 unsafe {
240 let data = {
241 if self.size > 0 {
242 let old_layout = Layout::from_size_align_unchecked(
243 self.size.wrapping_mul(size_of::<T>()),
244 align_of::<T>(),
245 );
246
247 alloc::realloc(self.data.as_ptr().cast(), old_layout, new_layout.size())
248 } else {
249 alloc::alloc(new_layout)
250 }
251 };
252
253 if data.is_null() {
254 return false;
255 }
256
257 self.data = NonNull::new_unchecked(data).cast();
258 }
259
260 true
261 }
262
263 #[must_use = "allocating is fallible and must be checked"]
264 fn reserve(&mut self, len: usize, additional: usize) -> bool {
265 debug_assert_ne!(size_of::<T>(), 0, "ZSTs should not get here");
266
267 let Some(required_cap) = len.checked_add(additional) else {
268 return false;
269 };
270
271 if self.size >= required_cap {
272 return true;
273 }
274
275 let cap = cmp::max(self.size * 2, required_cap);
276 let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap);
277
278 let Ok(new_layout) = Layout::array::<T>(cap) else {
279 return false;
280 };
281
282 if !self.realloc(new_layout) {
283 return false;
284 }
285
286 self.size = cap;
287 true
288 }
289
290 fn free(&mut self) {
291 if size_of::<T>() == 0 || self.size == 0 {
292 return;
293 }
294
295 unsafe {
298 let layout =
299 Layout::from_size_align_unchecked(self.size * size_of::<T>(), align_of::<T>());
300 alloc::dealloc(self.data.as_ptr().cast(), layout);
301 self.data = NonNull::dangling();
302 self.size = 0;
303 }
304 }
305}
306
307impl<T> Drop for GlobalAlloc<T> {
308 #[inline]
309 fn drop(&mut self) {
310 self.free();
311 }
312}