1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
//! The `alias-ptr` crate supplies the [`AliasPtr`] type,
//! which allows safely creating multiple pointers to the same heap-allocated memory,
//! and (unsafely) freeing the memory without reference counting overhead.

use std::ops::Deref;
use std::ptr::NonNull;

/// An untracked shared ownership pointer, pointing to heap memory manually freed.
///
/// The type `AliasPtr<T>` provides shared ownership of a value of type `T`,
/// allocated in the heap. Invoking [`copy`][AliasPtr::copy] on `AliasPtr` produces
/// a new `AliasPtr` instance, which points to the same allocation on the heap as the
/// source `AliasPtr`. When you call `delete` on any of the copies,
/// the value stored in that allocation is dropped,
/// and all of the copies can no longer be safely dereferenced.
///
/// `AliasPtr` is primarily intended as an unsafe building block for safe abstractions,
/// in order to avoid the runtime overhead of `Rc` or `Arc`
/// in cases where the lifetimes are known statically.
///
/// Shared references in Rust disallow mutation by default, and [`AliasPtr`]
/// is no exception: you cannot generally obtain a mutable reference to
/// something inside an [`AliasPtr`]. If you need mutability, put a `Cell`/`RefCell`
/// (not thread-safe), `Mutex`/`RwLock`/`Atomic` (thread-safe), or `UnsafeCell` (unsafe)
/// inside the `AliasPtr`.
///
/// ## Usage
///
/// For each `T` on the heap, you are responsible for calling `delete()`
/// on exactly one `AliasPtr` pointing to it,
/// and not dereferencing it or its aliases after.
///
/// In Rust terms, `AliasPtr<T>` acts like a `&T` that allows dangling and deletion,
/// a `Rc<T>` or `Arc<T>` with manual deletion,
/// or like a more convenient raw pointer which is assumed to be valid.
///
/// In C++ terms, `AliasPtr<T>` operates like `T*` or `T const*`, with shared ownership over `T`,
/// where the programmer decides which one to `delete`.
///
/// ## Thread Safety
///
/// Since `AliasPtr<T>` only exposes the same set of safe operations as a `&T`
/// (`delete()` is unsafe),
/// it would technically be sound to expose the same `Send`/`Sync` bounds as `&T`:
/// `impl Send/Sync for AliasPtr<T> where T: Sync`.
///
/// However, I added additional `T: Send` bounds for both `impl Send`/`Sync` (matching `Arc`),
/// as a lint to guard against sending or cloning an `AliasPtr<T>` to another thread,
/// then calling `delete()` there.
///
/// If these bounds are inappropriate for your data structure, you can `unsafe impl Send/Sync for`
/// your type containing `AliasPtr`.
///
/// ## Implementation
///
/// `AliasPtr<T>` has the same size as `&T`, and is interconvertible with a `Box<T>`.
///
/// `AliasPtr` wraps a raw pointer rather than a `&T`,
/// because it's not legal to pass a `&` into `Box::from_raw()`,
/// and a dangling `&` may be UB.
/// See ["How to Dismantle an Atomic Bomb"](http://blog.pnkfx.org/blog/2021/03/25/how-to-dismantle-an-atomic-bomb/)
/// for details.
#[repr(transparent)]
pub struct AliasPtr<T: ?Sized>(NonNull<T>);
// PhantomData is not necessary to prevent leaking Box<&'stack U> variables.
// Also read https://docs.rs/crate/ptr/0.2.2/source/src/lib.rs for reference.

impl<T: ?Sized> Clone for AliasPtr<T> {
    /// Copy the pointer without copying the underlying data.
    fn clone(&self) -> Self {
        Self(self.0)
    }
}

// TODO add support for custom allocators?
// Perhaps holding an allocator reference is inappropriate for a raw pointer workalike,
// so add a PhantomData(A) and accept an A in a "delete_alloc" function?
// Not sure.

impl<T: ?Sized> From<Box<T>> for AliasPtr<T> {
    fn from(item: Box<T>) -> Self {
        // Safety: pointer is obtained from Box::into_raw().
        unsafe { Self::from_raw(Box::into_raw(item)) }
    }
}

impl<T: Sized> AliasPtr<T> {
    /// Allocates memory on the heap and then places `x` into it.
    ///
    /// This doesn't actually allocate if `T` is zero-sized.
    ///
    /// # Examples
    ///
    /// ```
    /// # use alias_ptr::AliasPtr;
    /// let five = AliasPtr::new(5);
    /// ```
    pub fn new(x: T) -> AliasPtr<T> {
        AliasPtr::from(Box::new(x))
    }
}

impl<T: ?Sized> AliasPtr<T> {
    /// Constructs an `AliasPtr` from a raw pointer.
    ///
    /// # Safety
    ///
    /// `p` must be non-null and valid (its target is readable and writable).
    ///
    /// In order for calling `delete()` to be sound,
    /// `p` must be obtained from `Box::into_raw()`.
    pub unsafe fn from_raw(p: *mut T) -> Self {
        Self(NonNull::new_unchecked(p))
    }

    // TODO should some of these functions be turned into type-level functions
    // to avoid clashing with Deref?

    /// Copy the pointer without copying the underlying data.
    /// (This is equivalent to calling `clone()`.
    /// This type doesn't implement `Copy` to ensure all copies are explicit.)
    pub fn copy(&self) -> Self {
        self.clone()
    }

    /// Call the destructor of `T` and free the allocated memory.
    ///
    /// # Safety
    ///
    /// The `AliasPtr` must be derived from `Box::into_raw()` (from the global allocator).
    /// After calling `delete()`, neither self nor aliasing pointers
    /// can be safely dereferenced (a safe but unsound operation).
    ///
    /// This method should logically take `self` by move,
    /// but that would unfortunately prevent `drop(&mut Parent)` from calling `delete(self)`.
    /// `Drop` only provides `&mut Parent`, which doesn't allow moving fields out.
    /// For discussion, see ["Re-use struct fields on drop"](https://internals.rust-lang.org/t/re-use-struct-fields-on-drop-was-drop-mut-self-vs-drop-self/8594).
    pub unsafe fn delete(&mut self) {
        Box::from_raw(self.0.as_ptr());
    }

    /// Provides a raw pointer to the data.
    ///
    /// The pointer is valid until delete() is called on the `this` or any of its aliases.
    pub fn as_ptr(this: &Self) -> *const T {
        this.0.as_ptr()
    }
}

impl<T: ?Sized> Deref for AliasPtr<T> {
    type Target = T;
    fn deref(&self) -> &T {
        // Safety: AliasPtr is always constructed from a Box,
        // so can be dereferenced safely.
        // It is the responsibility of the user to never delete() a AliasPtr
        // then dereference it or its aliases afterwards.
        unsafe { &*self.0.as_ptr() }
    }
}

unsafe impl<T: ?Sized> Send for AliasPtr<T> where T: Send + Sync {}
unsafe impl<T: ?Sized> Sync for AliasPtr<T> where T: Send + Sync {}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::Cell;
    use std::mem::size_of;

    struct AliasedPair(AliasPtr<Cell<i32>>, AliasPtr<Cell<i32>>);

    impl AliasedPair {
        fn new(x: i32) -> AliasedPair {
            let x = AliasPtr::new(Cell::new(x));
            AliasedPair(x.copy(), x)
        }
    }

    impl Drop for AliasedPair {
        fn drop(&mut self) {
            unsafe {
                self.0.delete();
            }
        }
    }

    #[test]
    fn test_option_size_of() {
        assert_eq!(size_of::<usize>(), size_of::<AliasPtr<i32>>());
        assert_eq!(size_of::<usize>(), size_of::<Option<AliasPtr<i32>>>());
    }

    #[test]
    fn test_aliased_pair() {
        let pair = AliasedPair::new(1);
        pair.0.set(42);
        assert_eq!(pair.1.get(), 42);
    }

    // /// Does not compile, as expected.
    // fn f() -> AliasPtr<&'static i32> {
    //     let x = 1;
    //     let out = AliasPtr::new(&x) as AliasPtr<&'static i32>;
    //     out
    // }
}