safe_allocator_api/raw_alloc.rs
1//! A safe wrapper around low-level allocation primitives from `alloc::alloc`.
2//!
3//! This crate provides a safe interface for working with raw allocations while maintaining
4//! the same error handling semantics as the underlying allocation APIs.
5use crate::prelude::*;
6use core::fmt;
7use core::ptr::NonNull;
8
9/// A safe wrapper around a raw allocation with known layout.
10///
11/// # Safety
12///
13/// This type ensures that:
14/// - The wrapped pointer is always non-null and properly aligned
15/// - Memory is automatically deallocated when dropped
16/// - Reallocation maintains proper alignment and size constraints
17///
18/// # Example
19///
20/// ```rust
21/// # use core::alloc::Layout;
22/// use safe_allocator_api::RawAlloc;
23///
24/// // Create a new allocation of 1024 bytes
25/// let layout = Layout::array::<u8>(1024).unwrap();
26/// let mut alloc = RawAlloc::new(layout).expect("allocation failed");
27///
28/// // Write some data
29/// unsafe {
30/// core::ptr::write(alloc.as_mut_ptr(), 42u8);
31/// }
32///
33/// // Automatically deallocated when dropped
34/// ```
35pub struct RawAlloc<A: Allocator = Global> {
36 ptr: NonNull<[u8]>,
37 layout: Layout,
38 allocator: A,
39}
40
41impl<A: Allocator> RawAlloc<A> {
42 /// Creates a new allocation with the given layout using the provided allocator.
43 ///
44 /// This is equivalent to calling [`Allocator::allocate`] but provides automatic
45 /// cleanup when the allocation is no longer needed.
46 ///
47 /// # Arguments
48 ///
49 /// * `layout` - The desired memory layout
50 /// * `allocator` - The allocator to use
51 ///
52 /// # Errors
53 ///
54 /// Returns [`AllocError`] if the allocator reports an error or if the layout
55 /// has a size of 0.
56 ///
57 /// # Example
58 ///
59 /// ```rust
60 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
61 ///
62 /// use safe_allocator_api::prelude::*;
63 /// use safe_allocator_api::RawAlloc;
64 ///
65 /// let layout = Layout::new::<u64>();
66 /// let alloc = RawAlloc::new_in(layout, Global)?;
67 /// # Ok::<_, AllocError>(())
68 /// ```
69 pub fn new_in(layout: Layout, allocator: A) -> Result<Self, AllocError> {
70 if layout.size() == 0 {
71 return Err(AllocError);
72 }
73
74 let ptr = allocator.allocate(layout)?;
75
76 Ok(Self {
77 ptr,
78 layout,
79 allocator,
80 })
81 }
82
83 /// Creates a new zeroed allocation with the given layout using the provided allocator.
84 ///
85 /// This is equivalent to calling [`Allocator::allocate_zeroed`] but provides automatic
86 /// cleanup when the allocation is no longer needed.
87 ///
88 /// # Errors
89 ///
90 /// Returns [`AllocError`] if the allocator reports an error or if the layout
91 /// has a size of 0.
92 pub fn new_zeroed_in(layout: Layout, allocator: A) -> Result<Self, AllocError> {
93 if layout.size() == 0 {
94 return Err(AllocError);
95 }
96
97 let ptr = allocator.allocate_zeroed(layout)?;
98
99 Ok(Self {
100 ptr,
101 layout,
102 allocator,
103 })
104 }
105
106 /// Attempts to grow the allocation to the new layout.
107 ///
108 /// # Errors
109 ///
110 /// Returns [`AllocError`] if:
111 /// - The allocator reports an error
112 /// - The new layout has a size of 0
113 /// - The new size is smaller than the current size (use [`Self::shrink`] instead)
114 ///
115 /// # Example
116 ///
117 /// ```rust
118 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
119 ///
120 /// use safe_allocator_api::prelude::*;
121 /// use safe_allocator_api::RawAlloc;
122 ///
123 /// let layout = Layout::array::<u8>(100).unwrap();
124 /// let mut alloc = RawAlloc::new(layout)?;
125 ///
126 /// // Grow the allocation
127 /// let new_layout = Layout::array::<u8>(200).unwrap();
128 /// alloc.grow(new_layout)?;
129 /// # Ok::<_, AllocError>(())
130 /// ```
131 pub fn grow(&mut self, new_layout: Layout) -> Result<(), AllocError> {
132 if new_layout.size() == 0 {
133 return Err(AllocError);
134 }
135 if new_layout.size() <= self.layout.size() {
136 return Err(AllocError);
137 }
138
139 let new_ptr = unsafe {
140 self.allocator.grow(
141 NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
142 self.layout,
143 new_layout,
144 )?
145 };
146
147 self.ptr = new_ptr;
148 self.layout = new_layout;
149 Ok(())
150 }
151
152 /// Attempts to grow the allocation to the new layout, zeroing the additional memory.
153 ///
154 /// This is equivalent to [`Self::grow`] but ensures any additional memory is zeroed.
155 ///
156 /// # Errors
157 ///
158 /// Returns [`AllocError`] if:
159 /// - The allocator reports an error
160 /// - The new layout has a size of 0
161 /// - The new size is smaller than the current size (use [`Self::shrink`] instead)
162 pub fn grow_zeroed(&mut self, new_layout: Layout) -> Result<(), AllocError> {
163 if new_layout.size() == 0 {
164 return Err(AllocError);
165 }
166 if new_layout.size() <= self.layout.size() {
167 return Err(AllocError);
168 }
169
170 let new_ptr = unsafe {
171 self.allocator.grow_zeroed(
172 NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
173 self.layout,
174 new_layout,
175 )?
176 };
177
178 self.ptr = new_ptr;
179 self.layout = new_layout;
180 Ok(())
181 }
182
183 /// Attempts to shrink the allocation to the new layout.
184 ///
185 /// # Errors
186 ///
187 /// Returns [`AllocError`] if:
188 /// - The allocator reports an error
189 /// - The new layout has a size of 0
190 /// - The new size is larger than the current size (use [`Self::grow`] instead)
191 ///
192 /// # Example
193 ///
194 /// ```rust
195 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
196 ///
197 /// use safe_allocator_api::prelude::*;
198 /// use safe_allocator_api::RawAlloc;
199 ///
200 /// let layout = Layout::array::<u8>(200).unwrap();
201 /// let mut alloc = RawAlloc::new(layout)?;
202 ///
203 /// // Shrink the allocation
204 /// let new_layout = Layout::array::<u8>(100).unwrap();
205 /// alloc.shrink(new_layout)?;
206 /// # Ok::<_, AllocError>(())
207 /// ```
208 pub fn shrink(&mut self, new_layout: Layout) -> Result<(), AllocError> {
209 if new_layout.size() == 0 {
210 return Err(AllocError);
211 }
212 if new_layout.size() >= self.layout.size() {
213 return Err(AllocError);
214 }
215
216 let new_ptr = unsafe {
217 self.allocator.shrink(
218 NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
219 self.layout,
220 new_layout,
221 )?
222 };
223
224 self.ptr = new_ptr;
225 self.layout = new_layout;
226 Ok(())
227 }
228
229 /// Returns a raw pointer to the allocated memory.
230 ///
231 /// # Safety
232 ///
233 /// The caller must ensure that the memory is accessed according to
234 /// the original layout constraints.
235 pub fn as_ptr(&self) -> *const u8 {
236 self.ptr.as_ptr() as *const u8
237 }
238
239 /// Returns a raw mutable pointer to the allocated memory.
240 ///
241 /// # Safety
242 ///
243 /// The caller must ensure that the memory is accessed according to
244 /// the original layout constraints.
245 pub fn as_mut_ptr(&mut self) -> *mut u8 {
246 self.ptr.as_ptr() as *mut u8
247 }
248
249 /// Returns a slice reference to the allocated memory.
250 ///
251 /// This provides a safe interface to access the allocated memory as a byte slice.
252 ///
253 /// # Example
254 ///
255 /// ```rust
256 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
257 ///
258 /// use safe_allocator_api::prelude::*;
259 /// use safe_allocator_api::RawAlloc;
260 ///
261 /// let layout = Layout::array::<u8>(100).unwrap();
262 /// let alloc = RawAlloc::new(layout)?;
263 /// let slice = alloc.as_slice();
264 /// assert_eq!(slice.len(), 100);
265 /// # Ok::<_, AllocError>(())
266 /// ```
267 pub fn as_slice(&self) -> &[u8] {
268 unsafe { core::slice::from_raw_parts(self.as_ptr(), self.layout.size()) }
269 }
270
271 /// Returns a mutable slice reference to the allocated memory.
272 ///
273 /// This provides a safe interface to access the allocated memory as a mutable byte slice.
274 ///
275 /// # Example
276 ///
277 /// ```rust
278 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
279 ///
280 /// use safe_allocator_api::prelude::*;
281 /// use safe_allocator_api::RawAlloc;
282 ///
283 /// let layout = Layout::array::<u8>(100).unwrap();
284 /// let mut alloc = RawAlloc::new(layout)?;
285 /// let slice = alloc.as_mut_slice();
286 /// slice[0] = 42;
287 /// assert_eq!(slice[0], 42);
288 /// # Ok::<_, AllocError>(())
289 /// ```
290 pub fn as_mut_slice(&mut self) -> &mut [u8] {
291 unsafe { core::slice::from_raw_parts_mut(self.as_mut_ptr(), self.layout.size()) }
292 }
293
294 /// Returns the layout used for this allocation.
295 pub fn layout(&self) -> Layout {
296 self.layout
297 }
298
299 /// Represents the length of the allocation.
300 ///
301 /// # Remarks
302 ///
303 /// This is the length with which the allocation was created with, extracted
304 /// from the [`Layout`]; in practice, due to alignment, the number of available
305 /// bytes may be slightly larger in practice; but you shouldn't rely on that.
306 pub fn len(&self) -> usize {
307 self.layout.size()
308 }
309
310 /// Checks if the allocation is empty.
311 pub fn is_empty(&self) -> bool {
312 self.len() == 0
313 }
314}
315
316impl<A: Allocator> Drop for RawAlloc<A> {
317 fn drop(&mut self) {
318 unsafe {
319 self.allocator.deallocate(
320 NonNull::new_unchecked(self.ptr.as_ptr() as *mut u8),
321 self.layout,
322 );
323 }
324 }
325}
326
327impl<A: Allocator> fmt::Debug for RawAlloc<A> {
328 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329 f.debug_struct("RawAlloc")
330 .field("ptr", &self.ptr)
331 .field("layout", &self.layout)
332 .finish()
333 }
334}
335
336// Convenience constructors using the Global allocator
337impl RawAlloc {
338 /// Creates a new allocation with the given layout using the global allocator.
339 ///
340 /// This is equivalent to calling [`Self::new_in`] with the global allocator.
341 pub fn new(layout: Layout) -> Result<Self, AllocError> {
342 Self::new_in(layout, Global)
343 }
344
345 /// Creates a new zeroed allocation with the given layout using the global allocator.
346 ///
347 /// This is equivalent to calling [`Self::new_zeroed_in`] with the global allocator.
348 pub fn new_zeroed(layout: Layout) -> Result<Self, AllocError> {
349 Self::new_zeroed_in(layout, Global)
350 }
351}
352
353// Cannot implement Send + Sync automatically due to the raw pointer
354// Users must opt-in by implementing these traits based on their usage
355unsafe impl<A: Allocator> Send for RawAlloc<A> {}
356unsafe impl<A: Allocator> Sync for RawAlloc<A> {}
357
358#[cfg(test)]
359mod tests {
360 use super::*;
361 use alloc::vec::Vec;
362 use core::mem::size_of;
363
364 #[test]
365 fn zero_sized_alloc_returns_error() {
366 let layout = Layout::from_size_align(0, 1).unwrap();
367 assert!(RawAlloc::new(layout).is_err());
368 }
369
370 #[test]
371 fn basic_alloc_and_write() {
372 let layout = Layout::new::<u32>();
373 let mut alloc = RawAlloc::new(layout).unwrap();
374
375 unsafe {
376 core::ptr::write(alloc.as_mut_ptr() as *mut u32, 0xDEADBEEF);
377 assert_eq!(core::ptr::read(alloc.as_ptr() as *const u32), 0xDEADBEEF);
378 }
379 }
380
381 #[test]
382 fn zeroed_allocation() {
383 let size = 1024;
384 let layout = Layout::array::<u8>(size).unwrap();
385 let alloc = RawAlloc::new_zeroed(layout).unwrap();
386
387 unsafe {
388 let slice = core::slice::from_raw_parts(alloc.as_ptr(), size);
389 assert!(slice.iter().all(|&x| x == 0));
390 }
391 }
392
393 #[test]
394 fn custom_allocator() {
395 let layout = Layout::new::<i32>();
396 let alloc = RawAlloc::new_in(layout, Global).unwrap();
397 assert_eq!(alloc.layout().size(), size_of::<i32>());
398 }
399
400 #[test]
401 fn array_allocation() {
402 let elements = 100;
403 let layout = Layout::array::<u64>(elements).unwrap();
404 let mut alloc = RawAlloc::new(layout).unwrap();
405
406 unsafe {
407 let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr() as *mut u64, elements);
408
409 for (i, item) in slice.iter_mut().enumerate() {
410 *item = i as u64;
411 }
412
413 assert_eq!(slice[42], 42);
414 }
415 }
416
417 #[test]
418 fn alignment_requirements() {
419 let align = 64; // Test a large alignment
420 let size = 128;
421 let layout = Layout::from_size_align(size, align).unwrap();
422 let alloc = RawAlloc::new(layout).unwrap();
423
424 let addr = alloc.as_ptr() as usize;
425 assert_eq!(addr % align, 0, "Allocation not properly aligned");
426 }
427
428 #[test]
429 fn multiple_allocations() {
430 let layout = Layout::new::<u8>();
431 let mut allocations = Vec::new();
432
433 // Create many allocations to stress the allocator
434 for i in 0..100 {
435 let mut alloc = RawAlloc::new(layout).unwrap();
436 unsafe {
437 core::ptr::write(alloc.as_mut_ptr(), i as u8);
438 }
439 allocations.push(alloc);
440 }
441
442 // Verify each allocation is independent
443 for (i, alloc) in allocations.iter().enumerate() {
444 unsafe {
445 assert_eq!(core::ptr::read(alloc.as_ptr()), i as u8);
446 }
447 }
448 }
449
450 #[test]
451 fn oversized_allocation() {
452 // Try to allocate a very large size (but not so large it would definitely fail)
453 let layout = Layout::array::<u8>(1024 * 1024).unwrap();
454 let result = RawAlloc::new(layout);
455
456 // We don't assert success or failure here, as it depends on the system,
457 // but we verify it doesn't panic
458 let _ = result.is_ok();
459 }
460
461 #[test]
462 fn grow_allocation() {
463 let initial_size = 100;
464 let layout = Layout::array::<u8>(initial_size).unwrap();
465 let mut alloc = RawAlloc::new(layout).unwrap();
466
467 // Write some data
468 unsafe {
469 let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
470 slice[0] = 42;
471 }
472
473 // Grow the allocation
474 let new_size = 200;
475 let new_layout = Layout::array::<u8>(new_size).unwrap();
476 alloc.grow(new_layout).unwrap();
477
478 // Verify the data is preserved
479 unsafe {
480 let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
481 assert_eq!(slice[0], 42);
482 }
483 }
484
485 #[test]
486 fn grow_zeroed_allocation() {
487 let initial_size = 100;
488 let layout = Layout::array::<u8>(initial_size).unwrap();
489 let mut alloc = RawAlloc::new(layout).unwrap();
490
491 // Write some data
492 unsafe {
493 let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
494 slice[0] = 42;
495 }
496
497 // Grow the allocation
498 let new_size = 200;
499 let new_layout = Layout::array::<u8>(new_size).unwrap();
500 alloc.grow_zeroed(new_layout).unwrap();
501
502 unsafe {
503 let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
504 // Verify original data is preserved
505 assert_eq!(slice[0], 42);
506 // Verify new memory is zeroed
507 assert!(slice[initial_size..].iter().all(|&x| x == 0));
508 }
509 }
510
511 #[test]
512 fn shrink_allocation() {
513 let initial_size = 200;
514 let layout = Layout::array::<u8>(initial_size).unwrap();
515 let mut alloc = RawAlloc::new(layout).unwrap();
516
517 // Write some data
518 unsafe {
519 let slice = core::slice::from_raw_parts_mut(alloc.as_mut_ptr(), initial_size);
520 slice[0] = 42;
521 }
522
523 // Shrink the allocation
524 let new_size = 100;
525 let new_layout = Layout::array::<u8>(new_size).unwrap();
526 alloc.shrink(new_layout).unwrap();
527
528 // Verify the data is preserved
529 unsafe {
530 let slice = core::slice::from_raw_parts(alloc.as_ptr(), new_size);
531 assert_eq!(slice[0], 42);
532 }
533 }
534
535 #[test]
536 fn grow_zero_size_fails() {
537 let layout = Layout::array::<u8>(100).unwrap();
538 let mut alloc = RawAlloc::new(layout).unwrap();
539
540 let new_layout = Layout::from_size_align(0, 1).unwrap();
541 assert!(alloc.grow(new_layout).is_err());
542 }
543
544 #[test]
545 fn shrink_zero_size_fails() {
546 let layout = Layout::array::<u8>(100).unwrap();
547 let mut alloc = RawAlloc::new(layout).unwrap();
548
549 let new_layout = Layout::from_size_align(0, 1).unwrap();
550 assert!(alloc.shrink(new_layout).is_err());
551 }
552
553 #[test]
554 fn grow_smaller_size_fails() {
555 let layout = Layout::array::<u8>(200).unwrap();
556 let mut alloc = RawAlloc::new(layout).unwrap();
557
558 let new_layout = Layout::array::<u8>(100).unwrap();
559 assert!(alloc.grow(new_layout).is_err());
560 }
561
562 #[test]
563 fn shrink_larger_size_fails() {
564 let layout = Layout::array::<u8>(100).unwrap();
565 let mut alloc = RawAlloc::new(layout).unwrap();
566
567 let new_layout = Layout::array::<u8>(200).unwrap();
568 assert!(alloc.shrink(new_layout).is_err());
569 }
570}