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