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
use holochain_wasmer_common::*;

/// Allocate bytes that won't be dropped by the allocator.
/// Return the pointer to the leaked allocation so the host can write to it.
#[no_mangle]
#[inline(always)]
pub extern "C" fn __allocate(len: Len) -> GuestPtr {
    let dummy: Vec<u8> = Vec::with_capacity(len as usize);
    let ptr = dummy.as_ptr() as GuestPtr;
    let _ = core::mem::ManuallyDrop::new(dummy);
    ptr
}

/// Free an allocation.
/// Needed because we leak memory every time we call `__allocate` and `write_bytes`.
#[no_mangle]
#[inline(always)]
pub extern "C" fn __deallocate(guest_ptr: GuestPtr, len: Len) {
    let _: Vec<u8> =
        unsafe { Vec::from_raw_parts(guest_ptr as *mut u8, len as usize, len as usize) };
}

/// Attempt to consume bytes from a known guest_ptr and len.
///
/// Consume in this context means take ownership of previously forgotten data.
///
/// This needs to work for bytes written into the guest from the host and for bytes written with
/// the write_bytes() function within the guest.
#[inline(always)]
pub fn consume_bytes(guest_ptr: GuestPtr, len: Len) -> Vec<u8> {
    unsafe {
        Vec::from_raw_parts(
            // must match the pointer produced by the original allocation exactly
            guest_ptr as *mut u8,
            // this is the full length of the allocation as we want all the bytes
            len as usize,
            // must match the capacity set during the original allocation exactly
            len as usize,
        )
    }
}

/// Attempt to write a slice of bytes.
///
/// This is identical to the following:
/// - host has some slice of bytes
/// - host calls __allocate with the slice length
/// - guest returns GuestPtr to the host
/// - host writes the bytes into the guest at GuestPtr location
/// - host hands the GuestPtr back to the guest
///
/// In this case everything happens within the guest and a GuestPtr is returned if successful.
///
/// This also leaks the written bytes, exactly like the above process.
///
/// This facilitates the guest handing a GuestPtr back to the host as the _return_ value of guest
/// functions so that the host can read the _output_ of guest logic from a pointer.
///
/// The host MUST ensure either __deallocate is called or the entire wasm memory is dropped.
#[inline(always)]
pub fn write_bytes(v: Vec<u8>) -> GuestPtr {
    let ptr: GuestPtr = v.as_ptr() as GuestPtr;
    let _ = core::mem::ManuallyDrop::new(v);
    ptr
}