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
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 {
    write_bytes(Vec::with_capacity(
        // If `usize` is smaller than `u32` the host cannot support that so we
        // panic/unwrap.
        len.try_into().unwrap(),
    ))
}

/// 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 _ = consume_bytes(guest_ptr, len);
}

/// 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> {
    // If `usize` is smaller than `u32`, the host cannot support that so we
    // panic/unwrap.
    let len_usize: usize = len.try_into().unwrap();
    // This must be a Vec and not only a slice, because slices will fail to
    // deallocate memory properly when dropped.
    // Assumes length and capacity are the same, which is true if `__allocate` is
    // used to allocate memory for the vector.
    unsafe { std::vec::Vec::from_raw_parts(guest_ptr as *mut u8, len_usize, len_usize) }
}

/// Given an owned vector of bytes, leaks it and returns a pointer the host can
/// use to read the bytes. This does NOT handle the length of the bytes, so the
/// guest will need to track the length separately from leaking the vector.
///
/// 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.
/// If the host fails to tell the guest where and how many bytes to deallocate, then this leak
/// becomes permanent to the guest.
#[inline(always)]
pub fn write_bytes(v: Vec<u8>) -> GuestPtr {
    // This *const u8 cast to u32 is safe and the only way to get a raw pointer as a u32 afaik.
    // > e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
    // https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/casting-between-types.html#pointer-casts
    v.leak().as_ptr() as u32
}