pebble_skip/standard_c/
memory.rs

1use core::{
2	cmp::Ordering,
3	convert::TryInto,
4	intrinsics::drop_in_place,
5	mem::{needs_drop, size_of, size_of_val_raw, MaybeUninit},
6	ptr::NonNull,
7	slice,
8};
9use pebble_sys::{
10	prelude::*,
11	standard_c::memory::{self as sys_memory, void},
12};
13
14/// Allocates a heap memory slot for an instance of `T`.
15///
16/// The slot can safely be freed using [`pebble_sys::standard_c::memory::free`].
17///
18/// If `T` is zero-sized, then a valid slot is returned without allocation.
19///
20/// # Errors
21///
22/// If the allocation fails. Allocating for zero-sized `T` is infallible.
23pub fn malloc<'a, T>() -> Result<&'a mut MaybeUninit<T>, ()> {
24	match size_of::<T>() {
25		0 => Ok(unsafe { &mut *(NonNull::dangling().as_ptr()) }),
26		size => unsafe { sys_memory::malloc(size).cast_unchecked_mut() }.ok_or(()),
27	}
28}
29
30/// # Errors
31///
32/// Iff not enough heap memory could be allocated.
33pub fn malloc_buffer_uninit<'a>(len: usize) -> Result<&'a mut [MaybeUninit<u8>], ()> {
34	match len {
35		0 => Ok(unsafe {
36			slice::from_raw_parts_mut(NonNull::<u8>::dangling().as_ptr() as *mut _, 0)
37		}),
38		size => Ok(unsafe {
39			slice::from_raw_parts_mut(
40				sys_memory::malloc(size).cast_unchecked_mut().ok_or(())?,
41				size,
42			)
43		}),
44	}
45}
46
47/// Allocates a heap memory slot for `count` instances of `T`.
48///
49/// The slot can safely be freed using [`pebble_sys::standard_c::memory::free`].
50///
51/// If `T` is zero-sized, then a valid slot is returned without allocation.
52///
53/// # Errors
54///
55/// If the allocation fails. Allocating for zero-sized `T` is infallible.
56pub fn calloc<'a, T>(count: usize) -> Result<&'a mut [MaybeUninit<T>], ()> {
57	match size_of::<T>() {
58		0 => Ok(unsafe { slice::from_raw_parts_mut(NonNull::dangling().as_ptr(), count) }),
59		size => match unsafe {
60			sys_memory::calloc(count, size)
61				.map(|mem| slice::from_raw_parts_mut(mem.cast_unchecked_mut(), count))
62		} {
63			Some(uninit) => Ok(uninit),
64			None => Err(()),
65		},
66	}
67}
68
69/// The result of a successful [`resize_realloc`] call.
70pub enum ReallocOk<'a, T> {
71	ShrunkenOrEqual(&'a mut [T]),
72	Grown(&'a mut [MaybeUninit<T>]),
73}
74
75/// The result of a failed [`resize_realloc`] call.
76pub enum ReallocError<'a, T> {
77	CouldNotGrowOrMove(&'a mut [T]),
78	CouldNotShrink(&'a mut [MaybeUninit<T>]),
79}
80
81/// Resizes a buffer, possibly moving it in the process. If `new_len < buffer.len()`, that is if the buffer is to be shrunk, any extra elements are dropped before this is attempted.
82///
83/// In cases where `buffer.len() == new_len`, both [`Ok`] and [`Err`] contain the `&mut [T]` variant.  
84/// See [`ReallocOk`] and [`ReallocError`] for more information.
85///
86/// # Safety
87///
88/// This function is only safe iff `buffer` was obtained from an allocation function in this module.
89///
90/// # Errors
91///
92/// [`ReallocError`], iff the reallocation fails. If the buffer fails to grow, you get the original back.
93///
94/// If the buffer fails to shrink, you still get the original back, but any elements beyond the first `new_len` ones will have been dropped.
95///
96/// # Panics
97///
98/// If there is an arithmetic overflow regarding [`usize`] to [`isize`] conversions or pointer offset calculations.
99#[allow(clippy::type_complexity)]
100pub unsafe fn resize_realloc<'a, T>(
101	buffer: &'static mut [T],
102	new_len: usize,
103) -> Result<ReallocOk<'a, T>, ReallocError<'a, T>> {
104	let size = size_of::<T>();
105	let old_len = buffer.len();
106	let old_ptr = &mut buffer[0] as *mut T;
107	if needs_drop::<T>() && new_len < old_len {
108		for discarded in slice::from_raw_parts_mut(
109			old_ptr.offset(new_len.try_into().expect("`new_len` overflow")),
110			old_len - new_len,
111		) {
112			drop_in_place(discarded)
113		}
114	}
115	match sys_memory::realloc(
116		old_ptr as *mut _ as *mut void,
117		size.checked_mul(new_len).expect("size overflow"),
118	) {
119		Some(new_ptr) => Ok(if old_len >= new_len {
120			ReallocOk::ShrunkenOrEqual(slice::from_raw_parts_mut(
121				new_ptr as *mut _ as *mut T,
122				new_len,
123			))
124		} else {
125			ReallocOk::Grown(slice::from_raw_parts_mut(
126				new_ptr as *mut _ as *mut MaybeUninit<T>,
127				new_len,
128			))
129		}),
130		None => Err(if new_len >= old_len {
131			ReallocError::CouldNotGrowOrMove(slice::from_raw_parts_mut(old_ptr, old_len))
132		} else {
133			ReallocError::CouldNotShrink(slice::from_raw_parts_mut(
134				old_ptr as *mut _ as *mut MaybeUninit<T>,
135				old_len,
136			))
137		}),
138	}
139}
140
141/// Releases a heap memory slot after dropping the instance inside.
142///
143/// # Safety
144///
145/// This function is only safe if `slot` was obtained from an allocation function in this module.
146pub unsafe fn drop_free<T: ?Sized>(slot: &'static mut T) {
147	let ptr = slot as *mut T;
148	drop_in_place(ptr);
149	match size_of_val_raw(ptr) {
150		0 => (),
151		_ => sys_memory::free(&mut *(ptr as *mut _)),
152	}
153}
154
155/// Compares two data slices.
156///
157/// # Panics
158///
159/// Iff `slice1.len() != slice2.len()`.
160#[must_use]
161#[track_caller]
162pub fn memcmp(slice1: &[u8], slice2: &[u8]) -> Ordering {
163	if slice1.len() != slice2.len() {
164		panic!("Tried to memcmp slices of different sizes")
165	}
166
167	match unsafe { sys_memory::memcmp(slice1.upcast(), slice2.upcast(), slice1.len()) } {
168		i32::MIN..=-1 => Ordering::Greater,
169		0 => Ordering::Equal,
170		1..=i32::MAX => Ordering::Less,
171	}
172}
173
174/// # Safety
175///
176/// This is only safe with ![`Drop`] types.
177pub unsafe fn memcpy<T>(dest: &mut [T], src: &[T]) {
178	if src.len() != dest.len() {
179		panic!("Tried to memcpy between slices of different sizes")
180	}
181
182	sys_memory::memcpy(
183		(&mut *dest).upcast_mut(),
184		src.upcast(),
185		src.len() * size_of::<T>(),
186	);
187}
188
189/// # Safety
190///
191/// This is only safe with ![`Drop`] types.
192pub unsafe fn memcpy_uninit<T>(dest: &mut [MaybeUninit<T>], src: &[T]) {
193	if src.len() != dest.len() {
194		panic!("Tried to memcpy between slices of different sizes")
195	}
196
197	sys_memory::memcpy(
198		(&mut *dest).upcast_mut(),
199		src.upcast(),
200		src.len() * size_of::<T>(),
201	);
202}
203
204/// Sets all bytes in `dest` to `c`.
205pub fn memset(dest: &mut [u8], c: u8) {
206	let len = dest.len();
207	unsafe { sys_memory::memset((&mut *dest).upcast_mut(), c.into(), len) };
208}