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
//! These are helper methods for building C APIs that have "alloc" and "free" methods to handle memory allocation of Rust objects.
//!
//! The pair of methods `to_c_owned()` and `to_owned_from_c()` allows passing ownership of a Rust object to C and back.
//!
//! You can create a Rust object and pass it to C with `to_c_owned()`, and you don't need to keep the reference to the object anywhere in the Rust part of the program.
//!
//! Any object given to C via `to_c_owned()` has to be claimed back via `to_owned_from_c()`. You need to create a C function for deallocation of objects that then calls `to_owned_from_c()`.
//!
//! Objects owned by C can be borrowed back to the Rust land by using `&T` as `extern "C"`'s function type. If, for some reason, you declare functions to take a pointer you can get a reference via `as_ref_from_c()`, but this should usually be unneccessary.
//!
//! `mut` and `const` versions of methods are only for convenience and are interchangeable.

/// Takes a Box and returns a C pointer that can be given to a C program to use for as long as it wants.
///
///     use capi::ToCOwned;
///     struct Foo;
///
///     #[no_mangle]
///     unsafe extern "C" fn create_foo() -> *const Foo {
///         Box::new(Foo).to_c_owned()
///     }
pub trait ToCOwned<T: Send> {
    /// Takes a box and returns a const C pointer to box's content without freeing the box.
    ///
    /// To take ownership of the box back (and to let Rust free its content) use `ptr.to_owned_from_c()`.
    unsafe fn to_c_owned(self) -> *const T;

    /// Takes a mutable box and returns a C pointer to box's content without freeing the box.
    ///
    /// To take ownership of the box back (and to let Rust free its content) use `ptr.to_owned_from_c_mut()`.
    unsafe fn to_c_owned_mut(mut self) -> *mut T;
}

impl<T: Send> ToCOwned<T> for Box<T> {
    unsafe fn to_c_owned(self) -> *const T {
        let ptr = std::mem::transmute(&*self);
        std::mem::forget(self);
        return ptr;
    }

    unsafe fn to_c_owned_mut(mut self) -> *mut T {
        let ptr = std::mem::transmute(&mut *self);
        std::mem::forget(self);
        return ptr;
    }
}

/// Takes ownership back from C. The pointer must not be used after `to_owned_from_c()` is called.
///
/// Every call to `to_c_owned()` must have a corresponding one call to `to_owned_from_c()`.
///
/// If `to_owned_from_c()` is not called Rust will leak memory.
/// If `to_owned_from_c()` is called more than once on the same pointer the program will crash or corrupt data.
///
///     use capi::FromCOwned;
///     struct Foo;
///
///     #[no_mangle]
///     unsafe extern "C" fn destroy_foo(ptr: *const Foo) {
///         ptr.to_owned_from_c();
///     }

pub trait FromCOwned<T> {
    /// The pointer must not be null.
    ///
    /// The returned object will be owned by Rust and will be freed if you don't store a reference to it.
    unsafe fn to_owned_from_c(self) -> T;

    /// Lets you "peek" the content pointed to by the pointer *without* taking ownership back. You're borrowing the object from C.
    ///
    /// It's usually better to declare functions as taking Rust references (e.g. `extern "C" fn use_foo(obj: &Foo)`) instead of using this method.
    unsafe fn as_ref_from_c(&self) -> &T;
    unsafe fn as_ref_from_c_mut(&mut self) -> &mut T;
}

impl<T> FromCOwned<T> for *const T {
    unsafe fn to_owned_from_c(self) -> T {
        let obj: Box<T> = std::mem::transmute(self);
        *obj
    }

    unsafe fn as_ref_from_c(&self) -> &T {
        let obj: &T = std::mem::transmute(*self);
        obj
    }

    unsafe fn as_ref_from_c_mut(&mut self) -> &mut T {
        let obj: &mut T = std::mem::transmute(*self);
        obj
    }
}

impl<T> FromCOwned<T> for *mut T {
    #[inline]
    unsafe fn to_owned_from_c(self) -> T {
        (self as *const T).to_owned_from_c()
    }

    unsafe fn as_ref_from_c(&self) -> &T {
        let obj: &T = std::mem::transmute(*self);
        obj
    }

    unsafe fn as_ref_from_c_mut(&mut self) -> &mut T {
        let obj: &mut T = std::mem::transmute(*self);
        obj
    }
}

#[cfg(test)]
unsafe fn test_ptr_create() -> *const Vec<u32> {
    let x = vec![12345u32];
    Box::new(x).to_c_owned()
}

#[test]
fn pass_ownership() {
    unsafe {
        let ptr = test_ptr_create();
        {
            let peek = ptr.as_ref_from_c();
            assert_eq!(12345u32, peek[0]);
        }
        let y = ptr.to_owned_from_c();

        assert_eq!(12345u32, y[0]);
    }
}

#[test]
fn pass_ownership_mut() {
    let x = vec![12345u32];

    unsafe {
        let mut ptr = Box::new(x).to_c_owned_mut();
        {
            let peek = ptr.as_ref_from_c_mut();
            assert_eq!(12345u32, peek[0]);
            peek.push(34567);
        }
        let y = ptr.to_owned_from_c();

        assert_eq!(12345, y[0]);
        assert_eq!(34567, y[1]);
    }
}