musli_core/alloc/
system.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};
9
10#[derive(Clone, Copy)]
37#[non_exhaustive]
38pub struct System;
39
40impl System {
41 #[inline]
43 pub const fn new() -> Self {
44 Self
45 }
46
47 #[inline]
54 pub(crate) unsafe fn slice_from_raw_parts<T>(data: NonNull<T>, size: usize) -> SystemAlloc<T> {
55 SystemAlloc { data, size }
56 }
57}
58
59impl Default for System {
60 #[inline]
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66unsafe impl Allocator for System {
67 const IS_SYSTEM: bool = true;
68
69 type Alloc<T> = SystemAlloc<T>;
70
71 #[inline]
72 fn alloc<T>(self, value: T) -> Result<Self::Alloc<T>, AllocError> {
73 let mut raw = SystemAlloc::<T>::alloc()?;
74
75 if size_of::<T>() != 0 {
76 unsafe {
78 raw.as_mut_ptr().write(value);
79 }
80 }
81
82 Ok(raw)
83 }
84
85 #[inline]
86 fn alloc_empty<T>(self) -> Self::Alloc<T> {
87 SystemAlloc::DANGLING
88 }
89}
90
91pub struct SystemAlloc<T> {
93 data: NonNull<T>,
95 size: usize,
97}
98
99impl<T> SystemAlloc<T> {
100 #[must_use = "allocating is fallible and must be checked"]
106 fn alloc() -> Result<Self, AllocError> {
107 if size_of::<T>() == 0 {
108 return Ok(Self {
109 data: NonNull::dangling(),
110 size: 1,
111 });
112 }
113
114 unsafe {
115 let data = alloc::alloc(Layout::new::<T>());
116
117 if data.is_null() {
118 return Err(AllocError);
119 }
120
121 Ok(Self {
122 data: NonNull::new_unchecked(data).cast(),
123 size: 1,
124 })
125 }
126 }
127}
128
129unsafe impl<T> Send for SystemAlloc<T> where T: Send {}
130unsafe impl<T> Sync for SystemAlloc<T> where T: Sync {}
131
132impl<T> Alloc<T> for SystemAlloc<T> {
133 #[inline]
134 fn as_ptr(&self) -> *const T {
135 self.data.as_ptr().cast_const().cast()
136 }
137
138 #[inline]
139 fn as_mut_ptr(&mut self) -> *mut T {
140 self.data.as_ptr().cast()
141 }
142
143 #[inline]
144 fn capacity(&self) -> usize {
145 if size_of::<T>() == 0 {
146 usize::MAX
147 } else {
148 self.size
149 }
150 }
151
152 #[inline]
153 fn resize(&mut self, len: usize, additional: usize) -> Result<(), AllocError> {
154 if size_of::<T>() == 0 {
155 return Ok(());
156 }
157
158 if !self.reserve(len, additional) {
159 return Err(AllocError);
160 }
161
162 Ok(())
163 }
164
165 #[inline]
166 fn try_merge<B>(&mut self, _: usize, other: B, _: usize) -> Result<(), B>
167 where
168 B: Alloc<T>,
169 {
170 if size_of::<T>() == 0 {
171 return Ok(());
172 }
173
174 Err(other)
175 }
176}
177
178impl<T> SystemAlloc<T> {
179 const MIN_NON_ZERO_CAP: usize = if size_of::<T>() == 1 {
180 8
181 } else if size_of::<T>() <= 1024 {
182 4
183 } else {
184 1
185 };
186
187 const DANGLING: Self = Self {
188 data: NonNull::dangling(),
189 size: 0,
190 };
191
192 #[must_use = "allocating is fallible and must be checked"]
198 fn realloc(&mut self, new_layout: Layout) -> bool {
199 unsafe {
200 let data = {
201 if self.size > 0 {
202 let old_layout = Layout::from_size_align_unchecked(
203 self.size.wrapping_mul(size_of::<T>()),
204 align_of::<T>(),
205 );
206
207 alloc::realloc(self.data.as_ptr().cast(), old_layout, new_layout.size())
208 } else {
209 alloc::alloc(new_layout)
210 }
211 };
212
213 if data.is_null() {
214 return false;
215 }
216
217 self.data = NonNull::new_unchecked(data).cast();
218 }
219
220 true
221 }
222
223 #[must_use = "allocating is fallible and must be checked"]
224 fn reserve(&mut self, len: usize, additional: usize) -> bool {
225 debug_assert_ne!(size_of::<T>(), 0, "ZSTs should not get here");
226
227 let Some(required_cap) = len.checked_add(additional) else {
228 return false;
229 };
230
231 if self.size >= required_cap {
232 return true;
233 }
234
235 let cap = cmp::max(self.size * 2, required_cap);
236 let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap);
237
238 let Ok(new_layout) = Layout::array::<T>(cap) else {
239 return false;
240 };
241
242 if !self.realloc(new_layout) {
243 return false;
244 }
245
246 self.size = cap;
247 true
248 }
249
250 fn free(&mut self) {
251 if size_of::<T>() == 0 || self.size == 0 {
252 return;
253 }
254
255 unsafe {
258 let layout =
259 Layout::from_size_align_unchecked(self.size * size_of::<T>(), align_of::<T>());
260 alloc::dealloc(self.data.as_ptr().cast(), layout);
261 self.data = NonNull::dangling();
262 self.size = 0;
263 }
264 }
265}
266
267impl<T> Drop for SystemAlloc<T> {
268 #[inline]
269 fn drop(&mut self) {
270 self.free();
271 }
272}