|
| 1 | +# Foreign Function Interface |
| 2 | + |
| 3 | +Vortex is a file format that can be used by any execution engine. Nearly every programming language supports |
| 4 | +the C ABI (Application Binary Interface), so by providing an FFI interface to work with Vortex objects we can |
| 5 | +make it easy to support a variety of languages. |
| 6 | + |
| 7 | +## Design |
| 8 | + |
| 9 | +The FFI is designed to be very simple and follows a very object-oriented approach: |
| 10 | + |
| 11 | +- **Constructors** are simple C functions that return opaque pointers |
| 12 | +- **Methods** are functions that receive an opaque pointer as the first argument, followed by subsequent arguments. |
| 13 | + Methods may return a value or void. |
| 14 | +- **Destructors** free native resources (allocations, file handles, network sockets) and must be explicitly called by |
| 15 | + the foreign language to avoid leaking resources. |
| 16 | + |
| 17 | +Constructors will generally allocate rust memory, and destructors free that memory. |
| 18 | + |
| 19 | +For example: |
| 20 | + |
| 21 | +```c |
| 22 | +// DType is an opaque pointer to the Rust DType enum. |
| 23 | +// typedef void* DType; |
| 24 | + |
| 25 | + |
| 26 | +// Pointer to memory allocated by Rust (via `Box::new()`) |
| 27 | +DType d_i32 = DType_new(DTYPE_PRIMITIVE_I32, false); |
| 28 | + |
| 29 | +printf("nullable = %d\n", DType_nullable(d_i32)); |
| 30 | +// "nullable = 1" |
| 31 | + |
| 32 | +// Rust memory is freed, d_i32 is now a dangling pointer. |
| 33 | +DType_free(d_i32); |
| 34 | +``` |
| 35 | +
|
| 36 | +## C Strings |
| 37 | +
|
| 38 | +C strings are null-terminated, while Rust's are not. This means that unfortunately, we cannot simply return a pointer |
| 39 | +to a `&str` or `&String` but instead need to copy the data into a new allocation. Instead, methods that return a string |
| 40 | +should instead receive two arguments: |
| 41 | +a `*mut c_void` which is a pointer to the start of a buffer that is large enough to hold the largest string, and a |
| 42 | +`*mut c_int` to store the length of the buffer after writing. |
| 43 | +
|
| 44 | +This means that we can actually request a pointer instead |
| 45 | +
|
| 46 | +Because C and Rust have different string representations, functions that return Strings must instead receive |
| 47 | +a pointer to a buffer, and a pointer to an integer. Any `str` or `String` from Rust will be copied into the output |
| 48 | +buffer, |
0 commit comments