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}