Skip to main content

musli_core/alloc/
global.rs

1use 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, FromAllocator, GlobalAllocator, Vec};
9
10/// Global buffer that can be used in combination with an [`Allocator`].
11///
12/// This uses the global allocator.
13///
14/// # Examples
15///
16/// ```
17/// use musli::alloc::{Global, Vec};
18///
19/// let alloc = Global::new();
20///
21/// let mut buf1 = Vec::new_in(alloc);
22/// let mut buf2 = Vec::new_in(alloc);
23//
24/// buf1.extend_from_slice(b"Hello, ")?;
25/// buf2.extend_from_slice(b"world!")?;
26///
27/// assert_eq!(buf1.as_slice(), b"Hello, ");
28/// assert_eq!(buf2.as_slice(), b"world!");
29///
30/// buf1.extend(buf2);
31/// assert_eq!(buf1.as_slice(), b"Hello, world!");
32/// # Ok::<_, musli::alloc::AllocError>(())
33/// ```
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35#[non_exhaustive]
36pub struct Global;
37
38impl Global {
39    /// Construct a new global allocator.
40    #[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            // Zero-sized types are never actually allocated, so cloning must
66            // not go through the global allocator with a zero-sized layout.
67            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            // SAFETY: The layout assumption has already been checked.
79            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    unsafe 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
103/// The [`Global`] allocator carries no state, so it can be obtained from any
104/// allocator.
105impl<A> FromAllocator<A> for Global {
106    #[inline]
107    fn from_allocator(_: A) -> Self {
108        Self
109    }
110
111    #[inline]
112    fn __from_vec<T>(vec: Vec<T, A>) -> Result<Vec<T, Self>, Vec<T, A>>
113    where
114        A: Allocator,
115    {
116        // An allocation backed by the global allocator can be converted
117        // through a standard vector without copying.
118        Ok(Vec::from(vec.into_std()?))
119    }
120}
121
122unsafe impl Allocator for Global {
123    #[inline]
124    fn __do_not_implement() {}
125
126    const IS_GLOBAL: bool = true;
127
128    type Alloc<T> = GlobalAlloc<T>;
129
130    #[inline]
131    fn alloc<T>(self, value: T) -> Result<Self::Alloc<T>, AllocError> {
132        let mut raw = GlobalAlloc::<T>::alloc()?;
133
134        if size_of::<T>() != 0 {
135            // SAFETY: The above ensures the data has been allocated.
136            unsafe {
137                raw.as_mut_ptr().write(value);
138            }
139        }
140
141        Ok(raw)
142    }
143
144    #[inline]
145    fn alloc_empty<T>(self) -> Self::Alloc<T> {
146        GlobalAlloc::DANGLING
147    }
148}
149
150/// A vector-backed allocation.
151pub struct GlobalAlloc<T> {
152    /// Pointer to the allocated region.
153    data: NonNull<T>,
154    /// The size in number of `T` elements in the region.
155    size: usize,
156}
157
158impl<T> GlobalAlloc<T> {
159    /// Reallocate the region to the given capacity.
160    ///
161    /// # Safety
162    ///
163    /// The caller must ensure that the new capacity is valid per [`Layout`].
164    #[must_use = "allocating is fallible and must be checked"]
165    fn alloc() -> Result<Self, AllocError> {
166        if size_of::<T>() == 0 {
167            return Ok(Self {
168                data: NonNull::dangling(),
169                size: 1,
170            });
171        }
172
173        unsafe {
174            let data = alloc::alloc(Layout::new::<T>());
175
176            if data.is_null() {
177                return Err(AllocError);
178            }
179
180            Ok(Self {
181                data: NonNull::new_unchecked(data).cast(),
182                size: 1,
183            })
184        }
185    }
186}
187
188unsafe impl<T> Send for GlobalAlloc<T> where T: Send {}
189unsafe impl<T> Sync for GlobalAlloc<T> where T: Sync {}
190
191impl<T> Alloc<T> for GlobalAlloc<T> {
192    #[inline]
193    fn as_ptr(&self) -> *const T {
194        self.data.as_ptr().cast_const().cast()
195    }
196
197    #[inline]
198    fn as_mut_ptr(&mut self) -> *mut T {
199        self.data.as_ptr().cast()
200    }
201
202    #[inline]
203    fn capacity(&self) -> usize {
204        if size_of::<T>() == 0 {
205            usize::MAX
206        } else {
207            self.size
208        }
209    }
210
211    #[inline]
212    fn resize(&mut self, len: usize, additional: usize) -> Result<(), AllocError> {
213        if size_of::<T>() == 0 {
214            return Ok(());
215        }
216
217        if !self.reserve(len, additional) {
218            return Err(AllocError);
219        }
220
221        Ok(())
222    }
223
224    #[inline]
225    fn try_merge<B>(&mut self, _: usize, other: B, _: usize) -> Result<(), B>
226    where
227        B: Alloc<T>,
228    {
229        if size_of::<T>() == 0 {
230            return Ok(());
231        }
232
233        Err(other)
234    }
235}
236
237impl<T> GlobalAlloc<T> {
238    const MIN_NON_ZERO_CAP: usize = if size_of::<T>() == 1 {
239        8
240    } else if size_of::<T>() <= 1024 {
241        4
242    } else {
243        1
244    };
245
246    const DANGLING: Self = Self {
247        data: NonNull::dangling(),
248        size: 0,
249    };
250
251    /// Reallocate the region to the given capacity.
252    ///
253    /// # Safety
254    ///
255    /// The caller must ensure that the new capacity is valid per [`Layout`].
256    #[must_use = "allocating is fallible and must be checked"]
257    fn realloc(&mut self, new_layout: Layout) -> bool {
258        unsafe {
259            let data = {
260                if self.size > 0 {
261                    let old_layout = Layout::from_size_align_unchecked(
262                        self.size.wrapping_mul(size_of::<T>()),
263                        align_of::<T>(),
264                    );
265
266                    alloc::realloc(self.data.as_ptr().cast(), old_layout, new_layout.size())
267                } else {
268                    alloc::alloc(new_layout)
269                }
270            };
271
272            if data.is_null() {
273                return false;
274            }
275
276            self.data = NonNull::new_unchecked(data).cast();
277        }
278
279        true
280    }
281
282    #[must_use = "allocating is fallible and must be checked"]
283    fn reserve(&mut self, len: usize, additional: usize) -> bool {
284        debug_assert_ne!(size_of::<T>(), 0, "ZSTs should not get here");
285
286        let Some(required_cap) = len.checked_add(additional) else {
287            return false;
288        };
289
290        if self.size >= required_cap {
291            return true;
292        }
293
294        let cap = cmp::max(self.size * 2, required_cap);
295        let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap);
296
297        let Ok(new_layout) = Layout::array::<T>(cap) else {
298            return false;
299        };
300
301        if !self.realloc(new_layout) {
302            return false;
303        }
304
305        self.size = cap;
306        true
307    }
308
309    fn free(&mut self) {
310        if size_of::<T>() == 0 || self.size == 0 {
311            return;
312        }
313
314        // SAFETY: Layout assumptions are correctly encoded in the type as
315        // it was being allocated or grown.
316        unsafe {
317            let layout =
318                Layout::from_size_align_unchecked(self.size * size_of::<T>(), align_of::<T>());
319            alloc::dealloc(self.data.as_ptr().cast(), layout);
320            self.data = NonNull::dangling();
321            self.size = 0;
322        }
323    }
324}
325
326impl<T> Drop for GlobalAlloc<T> {
327    #[inline]
328    fn drop(&mut self) {
329        self.free();
330    }
331}