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 (Custom Allocators) 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 Rust Documentation

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.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.