malloced/lib.rs
1//! A `malloc`-ed box pointer type, brought to you by
2//! [@NikolaiVazquez](https://twitter.com/NikolaiVazquez)!
3//!
4//! # Table of Contents
5//!
6//! 1. [Donate](#donate)
7//! 2. [Usage](#usage)
8//! 3. [MSRV](#msrv)
9//! 4. [FFI Safety](#ffi-safety)
10//! 5. [Alternatives](#alternatives)
11//! 6. [License](#license)
12//!
13//! # Donate
14//!
15//! If this project is useful to you, please consider
16//! [sponsoring me](https://github.com/sponsors/nvzqz) or
17//! [donating directly](https://www.paypal.me/nvzqz)!
18//!
19//! Doing so enables me to create high-quality open source software like this. ❤️
20//!
21//! # Usage
22//!
23//! This library is available [on crates.io](https://crates.io/crates/malloced) and
24//! can be used by adding the following to your project's
25//! [`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html):
26//!
27//! ```toml
28//! [dependencies]
29//! malloced = "1.3.1"
30//! ```
31//!
32//! The star of the show is [`Malloced`], [`Box`]-like pointer that calls `free` on
33//! [`Drop`]:
34//!
35//! ```rust
36//! use malloced::Malloced;
37//! ```
38//!
39//! # MSRV
40//!
41//! This library's minimum supported Rust version (MSRV) is 1.64. A new version
42//! requirement would result in a minor version update.
43//!
44//! # FFI Safety
45//!
46//! `Malloced<T>` is a `#[repr(transparent)]` wrapper over `NonNull<T>`, so it can
47//! be safely used in C FFI. For example, the following is safe and even compiles
48//! with the `improper_ctypes` lint enabled:
49//!
50//! ```rust
51//! # use malloced::Malloced;
52//! #[deny(improper_ctypes)]
53//! extern "C" {
54//! fn my_array_malloc() -> Malloced<[u8; 32]>;
55//! }
56//! ```
57//!
58//! # Alternatives
59//!
60//! - [`malloc_buf`](https://docs.rs/malloc_buf)
61//! - [`mbox`](https://docs.rs/mbox)
62//!
63//! # License
64//!
65//! This project is released under either
66//! [MIT License](https://github.com/nvzqz/malloced/blob/master/LICENSE-MIT) or
67//! [Apache License (Version 2.0)](https://github.com/nvzqz/malloced/blob/master/LICENSE-APACHE)
68//! at your choosing.
69//!
70//! [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html
71//! [`Drop`]: https://doc.rust-lang.org/std/ops/trait.Drop.html
72//! [`Malloced`]: struct.Malloced.html
73
74#![cfg_attr(not(feature = "std"), no_std)]
75
76#[cfg(test)]
77extern crate alloc;
78
79#[cfg(feature = "std")]
80use std as core;
81
82use core::{
83 any::Any,
84 ffi::{c_char, CStr},
85 marker::PhantomData,
86 mem,
87 mem::ManuallyDrop,
88 pin::Pin,
89 ptr::NonNull,
90};
91
92mod impls;
93mod iter;
94mod sys;
95
96pub use iter::*;
97
98/// A pointer type for `malloc`-ed heap allocation.
99///
100/// # Memory layout
101///
102/// So long as `T: Sized`, a `Malloced<T>` is guaranteed to be represented as a
103/// single pointer and is also ABI-compatible with C pointers (i.e. the C type
104/// `T*`). This means that if you have extern "C" Rust functions that will be
105/// called from C, you can define those Rust functions using `Malloced<T>`
106/// types, and use `T*` as corresponding type on the C side.
107///
108/// Regardless if `T: Sized`, a `Malloced<T>` is guaranteed to be ABI-compatible
109/// with [`NonNull<T>`](https://doc.rust-lang.org/std/ptr/struct.NonNull.html).
110#[repr(transparent)]
111pub struct Malloced<T: ?Sized> {
112 ptr: NonNull<T>,
113
114 // Marks ownership of an instance of T.
115 _marker: PhantomData<T>,
116}
117
118impl<T> IntoIterator for Malloced<[T]> {
119 type Item = T;
120 type IntoIter = SliceIter<T>;
121
122 #[inline]
123 fn into_iter(self) -> Self::IntoIter {
124 unsafe {
125 let buf = self.ptr.cast::<T>();
126
127 let len = self.len();
128
129 mem::forget(self);
130
131 let ptr = buf.as_ptr();
132
133 let end = if mem::size_of::<T>() == 0 {
134 // Purposefully don't use `ptr.offset` because for slices with
135 // 0-size elements this would return the same pointer.
136 //
137 // Use wrapping arithmetic to avoid the requirement of the
138 // result pointer being in the same allocation.
139 (ptr as *mut i8).wrapping_add(len) as *mut T
140 } else {
141 ptr.add(len)
142 };
143
144 SliceIter {
145 buf,
146 marker: PhantomData,
147 ptr,
148 end,
149 }
150 }
151 }
152}
153
154/// Testing helpers.
155#[cfg(test)]
156impl<T> Malloced<[T]> {
157 fn alloc(values: &[T]) -> Option<Self>
158 where
159 T: Copy,
160 {
161 let value_size = mem::size_of::<T>();
162 let alloc_size = values.len().checked_mul(value_size.max(1))?;
163
164 unsafe {
165 let buf = sys::malloc(alloc_size).cast::<T>();
166 if buf.is_null() {
167 return None;
168 }
169
170 for (i, &value) in values.iter().enumerate() {
171 let ptr: *mut T = if value_size == 0 {
172 buf.cast::<u8>().add(i).cast()
173 } else {
174 buf.add(i)
175 };
176
177 ptr.write(value);
178 }
179
180 Some(Malloced::slice_from_raw_parts(buf, values.len()))
181 }
182 }
183}
184
185impl<T: ?Sized> Malloced<T> {
186 /// Constructs an instance from a raw `malloc`-ed pointer.
187 ///
188 /// # Safety
189 ///
190 /// The data referenced by `ptr` must be valid and must have been allocated
191 /// by `malloc` so that it can be `free`-d on
192 /// [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html).
193 #[inline]
194 pub unsafe fn from_raw(ptr: *mut T) -> Self {
195 Self {
196 ptr: NonNull::new_unchecked(ptr),
197 _marker: PhantomData,
198 }
199 }
200
201 /// Consumes the instance, returning a wrapped raw pointer.
202 ///
203 /// The pointer will be properly aligned and non-null.
204 #[inline]
205 pub fn into_raw(this: Self) -> *mut T {
206 Self::leak(this)
207 }
208
209 /// Converts a `Malloced<T>` into a `Pin<Malloced<T>>`
210 ///
211 /// This conversion does not allocate on the heap and happens in place.
212 ///
213 /// This is also available via
214 /// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html).
215 #[inline]
216 pub fn into_pin(this: Self) -> Pin<Malloced<T>> {
217 // SAFETY: It's not possible to move or replace the insides of a
218 // `Pin<Malloced<T>>` when `T: !Unpin`, so it's safe to pin it directly
219 // without any additional requirements.
220 unsafe { Pin::new_unchecked(this) }
221 }
222
223 /// Consumes and leaks the instance, returning a mutable reference,
224 /// `&'a mut T`.
225 ///
226 /// Note that the type `T` must outlive the chosen lifetime `'a`. If the
227 /// type has only static references, or none at all, then this may be chosen
228 /// to be `'static`.
229 ///
230 /// This function is mainly useful for data that lives for the remainder of
231 /// the program's life. Dropping the returned reference will cause a memory
232 /// leak. If this is not acceptable, the reference should first be wrapped
233 /// with the [`Malloced::from_raw`](#method.from_raw) function producing a
234 /// `Malloced`. This `Malloced` can then be dropped which will properly
235 /// destroy `T` and `free` the allocated memory.
236 ///
237 /// Note: this is an associated function, which means that you have to call
238 /// it as `Malloced::leak(this)` instead of `this.leak()`. This is so that
239 /// there is no conflict with a method on the inner type.
240 #[inline]
241 pub fn leak<'a>(this: Self) -> &'a mut T
242 where
243 T: 'a,
244 {
245 unsafe { &mut *ManuallyDrop::new(this).ptr.as_ptr() }
246 }
247
248 /// Returns an immutable raw pointer to the data.
249 #[inline]
250 pub fn as_ptr(this: &Self) -> *const T {
251 this.ptr.as_ptr()
252 }
253
254 /// Returns a mutable raw pointer to the data.
255 #[inline]
256 pub fn as_mut_ptr(this: &mut Self) -> *mut T {
257 this.ptr.as_ptr()
258 }
259
260 // TODO: Implement `core::ops::CoerceUnsized`.
261 // See https://github.com/rust-lang/rust/issues/27732.
262
263 /// Erases the static type `T`.
264 #[inline]
265 pub fn into_any(this: Self) -> Malloced<dyn Any>
266 where
267 T: Sized + Any,
268 {
269 let ptr = this.ptr.as_ptr() as *mut dyn Any;
270 mem::forget(this);
271 unsafe { Malloced::from_raw(ptr) }
272 }
273
274 /// Erases the static type `T`.
275 #[inline]
276 pub fn into_any_send(this: Self) -> Malloced<dyn Any + Send + Sync>
277 where
278 T: Sized + Any + Send + Sync,
279 {
280 let ptr = this.ptr.as_ptr() as *mut (dyn Any + Send + Sync);
281 mem::forget(this);
282 unsafe { Malloced::from_raw(ptr) }
283 }
284}
285
286impl<T> Malloced<[T]> {
287 /// Constructs an instance for a slice from a pointer and a length.
288 ///
289 /// # Safety
290 ///
291 /// Behavior is undefined if any of the following conditions are violated:
292 ///
293 /// - `data` must have been allocated by `malloc` so that it can be `free`-d
294 /// on [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html).
295 ///
296 /// - `data` must be
297 /// [valid](https://doc.rust-lang.org/std/ptr/index.html#safety) for both
298 /// reads and writes for `len * mem::size_of::<T>()` many bytes, and it
299 /// must be properly aligned. This means in particular:
300 ///
301 /// - The entire memory range of this slice must be contained within a
302 /// single allocated object! Slices can never span across multiple
303 /// allocated objects.
304 ///
305 /// - `data` must be non-null and aligned even for zero-length slices.
306 /// One reason for this is that enum layout optimizations may rely on
307 /// references (including slices of any length) being aligned and
308 /// non-null to distinguish them from other data. You can obtain a
309 /// pointer that is usable as `data` for zero-length slices using
310 /// [`NonNull::dangling()`](https://doc.rust-lang.org/std/ptr/struct.NonNull.html#method.dangling).
311 ///
312 /// - `data` must point to `len` consecutive properly initialized values of
313 /// type `T`.
314 ///
315 /// - The total size `len * mem::size_of::<T>()` of the slice must be no
316 /// larger than `isize::MAX`. See the safety documentation of
317 /// [`pointer::offset`](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset).
318 ///
319 /// See
320 /// [`slice::from_raw_parts_mut`](https://doc.rust-lang.org/std/slice/fn.from_raw_parts_mut.html)
321 /// for details.
322 #[inline]
323 pub unsafe fn slice_from_raw_parts(data: *mut T, len: usize) -> Self {
324 Self::from_raw(core::ptr::slice_from_raw_parts_mut(data, len))
325 }
326}
327
328impl Malloced<CStr> {
329 /// Wraps a raw `malloc`ed C string with a safe owned C string wrapper.
330 ///
331 /// # Safety
332 ///
333 /// See [`CStr::from_ptr` safety docs](CStr::from_ptr).
334 #[inline]
335 pub unsafe fn from_ptr(ptr: *mut c_char) -> Self {
336 // If `&CStr` is a thin pointer, use a dummy length that is discarded.
337 let len = if mem::size_of::<*mut CStr>() == mem::size_of::<*mut c_char>() {
338 1
339 } else {
340 CStr::from_ptr(ptr).to_bytes_with_nul().len()
341 };
342
343 let ptr = core::ptr::slice_from_raw_parts_mut(ptr, len) as *mut CStr;
344
345 Self::from_raw(ptr)
346 }
347}
348
349impl Malloced<dyn Any> {
350 /// Attempt to downcast the instance to a concrete type.
351 #[inline]
352 pub fn downcast<T: Any>(self) -> Result<Malloced<T>, Self> {
353 if self.is::<T>() {
354 let raw: *mut dyn Any = Malloced::into_raw(self);
355 Ok(unsafe { Malloced::from_raw(raw as *mut T) })
356 } else {
357 Err(self)
358 }
359 }
360}
361
362impl Malloced<dyn Any + Send> {
363 /// Attempt to downcast the instance to a concrete type.
364 #[inline]
365 pub fn downcast<T: Any>(self) -> Result<Malloced<T>, Self> {
366 if self.is::<T>() {
367 let raw: *mut (dyn Any + Send) = Malloced::into_raw(self);
368 Ok(unsafe { Malloced::from_raw(raw as *mut T) })
369 } else {
370 Err(self)
371 }
372 }
373}
374
375impl Malloced<dyn Any + Send + Sync> {
376 /// Attempt to downcast the instance to a concrete type.
377 #[inline]
378 pub fn downcast<T: Any>(self) -> Result<Malloced<T>, Self> {
379 if self.is::<T>() {
380 let raw: *mut (dyn Any + Send + Sync) = Malloced::into_raw(self);
381 Ok(unsafe { Malloced::from_raw(raw as *mut T) })
382 } else {
383 Err(self)
384 }
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 mod c_str {
393 use super::*;
394
395 #[test]
396 fn from_ptr() {
397 let buf = Malloced::<[c_char]>::alloc(&[b'h' as _, b'i' as _, 0]).unwrap();
398 let ptr = ManuallyDrop::new(buf).ptr.as_ptr() as *mut c_char;
399
400 let result = unsafe { Malloced::<CStr>::from_ptr(ptr) };
401 assert_eq!(result.to_bytes(), b"hi");
402 }
403 }
404}