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
use std::alloc::alloc;
use std::alloc::dealloc;
use std::alloc::Layout;
use std::cmp::max;
use std::isize;
use std::mem::align_of;
use std::mem::size_of;
use std::os::raw::c_void;
use std::ptr::null_mut;

const LAYOUT_ALIGNMENT: usize = align_of::<Layout>();
const LAYOUT_SIZE: usize = size_of::<Layout>();

#[no_mangle]
pub unsafe extern "C" fn rust_cpp_new(
    size: usize,
    alignment: usize,
) -> *mut c_void {
    let offset = max(LAYOUT_ALIGNMENT, LAYOUT_SIZE);
    let new_alignment = max(offset, alignment);
    let new_size = size + new_alignment;

    if new_alignment >= (isize::MAX as usize) {
        null_mut()
    } else {
        match Layout::from_size_align(new_size, new_alignment) {
            Ok(layout) => {
                let allocation = alloc(layout);
                let result_allocation =
                    allocation.offset(new_alignment as isize);
                let hidden_allocation =
                    result_allocation.offset(-(offset as isize)) as *mut Layout;
                hidden_allocation.write(layout);

                result_allocation as *mut c_void
            }
            Err(_) => null_mut(),
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn rust_cpp_delete(payload: *const c_void) {
    let offset = max(LAYOUT_ALIGNMENT, LAYOUT_SIZE);

    if !payload.is_null() {
        let result_allocation = payload as *mut u8;
        let hidden_allocation =
            result_allocation.offset(-(offset as isize)) as *const Layout;
        let layout = hidden_allocation.read();
        let allocation = result_allocation.offset(-(layout.align() as isize));
        dealloc(allocation, layout);
    }
}

#[link(name = "cpp_new")]
extern "C" {}

#[cfg(test)]
mod tests {
    extern crate cpp_new_simple_cpp_lib;

    use self::cpp_new_simple_cpp_lib::*;

    #[test]
    fn new() {
        let foo = safe_create_foo();

        assert_eq!(foo.i, 42);
        assert_eq!(foo.f, 42.0);
        assert_eq!(foo.c, 42);

        safe_destroy_foo(foo);
    }

    #[test]
    fn array_new() {
        let foos = safe_create_foos(512);

        for foo in foos.iter() {
            assert_eq!(foo.i, 42);
            assert_eq!(foo.f, 42.0);
            assert_eq!(foo.c, 42);
        }

        safe_destroy_foos(foos);
    }
}