Custom allocator based on externally provided function pointers

Hello,

I'm trying to implement a plugin (dynamic library/dylib) for another (closed source) program.

The API I have to implement is provided in c, and I am pretty used to working with c, but took this as a challenge to implement it in Rust (and learn Rust on the way).

Basically I must implement an Init function that's called by the program, one of the parameters is a pointer to a struct containing function pointers, including alloc/free/realloc.

I'm trying to figure out how to write a custom allocator that will use those provided functions, and enforce it's use across the whole lib.

Due to licensing I cannot post the API, but here's the gist of it:

/* C Header */

typedef struct {
  void  (*Free)        (void* p);
  void* (*Alloc)       (unsigned size);
  void* (*Realloc)     (void* p, unsigned size);
} api_t;

int Init(api_t const *p_api);

// Other API functions...

Here's a snippet of what I've got so far in Rust:

//! lib.rs

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct api_t {
    pub Free: Option<unsafe extern "C" fn(p: *mut c_void)>,
    pub Alloc: Option<unsafe extern "C" fn(size: c_uint) -> *mut c_void>,
    pub Realloc: Option<unsafe extern "C" fn(p: *mut c_void, size: c_uint) -> *mut c_void>
}

static mut API:api_t = api_t {
    Free: Option::None,
    Alloc: Option::None,
    Realloc: Option::None,
};

#[no_mangle]
pub extern "C" fn Init(p_api: *const api_t) -> c_int {
    if p_api.is_null() {
        return -1;
    }

    unsafe {
        API = *p_api;
    }

    1
}

It is guaranteed that Init is the first function that gets called from my lib.

I considered writing a custom allocator (https://doc.rust-lang.org/1.9.0/book/custom-allocators.html) as a nested crate in my project, and add some kind of init function to it, which will get an immutable copy of the API struct and keep it as a static var, similarly to what I did in the snippet above.

Would that be feasible?

Any help would be greatly appreciated.

Your struct is missing #[repr(C)]. Without it Rust structs are incompatible with C.

Don't write these things by hand. Use bindgen.


https://doc.rust-lang.org/1.9.0/
                          ^^^^^

Sadly, search engines have pointed you to an archived historical version of the documentation. The correct up to date documentation is at https://doc.rust-lang.org/stable/

Sorry I missed that when copying the code, since I didn't want to copy all of the code.

Yes I used bindgen to generate all the bindings.

Fixed the snippet.
Thanks for the link.

Aha!

It's very simple now, just have to implement std::alloc::GlobalAlloc.

Thanks a lot!

Yes, GlobalAlloc is the one. You'll have to pay attention to alignment in the Layout object.

C allocators will usually give you blocks aligned to the biggest type in platform's ABI. Rust supports #[align(N)] so theoretically types may ask for more than that. So either abort() or add hacks to support them.

Thanks, I'll remember to take that into account.